Compare commits

..

43 commits
main ... stable

Author SHA1 Message Date
bors
01f6ddf758 Auto merge of #152450 - cuviper:stable-next, r=cuviper
[stable] prepare Rust 1.93.1

This includes the following backported fixes:

- Don't try to recover keyword as non-keyword identifier rust-lang/rust#150590
- Fix `panicking_unwrap` FP on field access with implicit deref rust-lang/rust-clippy#16196
- Revert "Update wasm-related dependencies in CI" rust-lang/rust#152259

And these are just to avoid recent CI issues:

- Remove rustdoc GUI flaky test rust-lang/rust#152116
- Remove the 4 failing tests from rustdoc-gui rust-lang/rust#152194

r? @rust-lang/release
@rustbot ping relnotes-interest-group
2026-02-11 07:30:54 +00:00
Josh Stone
674ccdd847 Release 1.93.1 2026-02-10 10:01:22 -08:00
Josh Stone
f0867bf650 Sync release note changes from main 2026-02-10 09:13:00 -08:00
Jonathan Brouwer
b8cc170b70 Remove the 4 failing tests from rustdoc-gui
(cherry picked from commit bce8c00e2f)
2026-02-10 09:07:27 -08:00
Guillaume Gomez
128b1c9f64 Remove rustdoc GUI flaky test
(cherry picked from commit 607ac4bb84)
2026-02-10 09:07:00 -08:00
Alex Crichton
f8cf317da3 Revert "Update wasm-related dependencies in CI"
This reverts commit 4c6e41cb53.
2026-02-10 09:06:32 -08:00
Linshu Yang
9c13ace16d fix: panicking_unwrap FP on field access with implicit deref
(cherry picked from commit c905503474)
2026-02-10 09:05:27 -08:00
Jieyou Xu
feb759bb79 Don't try to recover keyword as non-keyword identifier
There's no sensible recovery scheme here, giving up the recovery is the
right thing to do.

(cherry picked from commit 79c47278f1)
2026-02-10 08:55:47 -08:00
Jieyou Xu
f691f9a0ec Add regression tests for keyword-in-identifier-position recovery ICE
... in macro invocations.

Issue: <https://github.com/rust-lang/rust/issues/149692>.
(cherry picked from commit 31531b3665)
2026-02-10 08:55:47 -08:00
bors
254b59607d Auto merge of #151369 - Mark-Simulacrum:stable-next, r=Mark-Simulacrum
[stable] 1.93.0 release

https://forge.rust-lang.org/release/process.html#stable-pr

r? @Mark-Simulacrum
2026-01-19 16:34:28 +00:00
Mark Rousskov
01ad1d67a6 Bump to stable release 2026-01-19 09:20:55 -05:00
Mark Rousskov
641c209049 Add latest release note copy 2026-01-19 09:20:19 -05:00
bors
a741008f5b Auto merge of #151270 - Mark-Simulacrum:beta-backport, r=Mark-Simulacrum
[beta] backports

*  [beta] Disable SimplifyComparisonIntegral rust-lang/rust#151267 
*  Use the old homu bors e-mail address again rust-lang/rust#150959 

Does not backport other nominated PRs:

* Only use SSA locals in SimplifyComparisonIntegral rust-lang/rust#150925  (replaced with the disablement PR)
* Don't try to evaluate const blocks during constant promotion rust-lang/rust#150557 (backport essentially denied at this point)
* Use realstd current thread static variables in tests rust-lang/rust#150131 (as far as I can tell, this only affects internal std tests and hasn't landed in time for backport)

r? @Mark-Simulacrum
2026-01-18 04:13:20 +00:00
Ben Kimock
6feceec6b3 Disable SimplifyComparisonIntegral 2026-01-17 15:39:57 -05:00
Jakub Beránek
2b8a999ac4 Use both bors e-mails for CI postprocessing git lookup 2026-01-17 15:38:03 -05:00
Jakub Beránek
4c28b43a94 Revert bors email to the original homu one 2026-01-17 15:36:12 -05:00
Jakub Beránek
a8201d341a Remove references to bors2 2026-01-17 15:36:11 -05:00
bors
9a4aba12f3 Auto merge of #151241 - fmease:beta-rustdoc-dont-eval-assoc-consts, r=fmease
[beta] rustdoc: Stop unconditionally evaluating the initializer of associated consts

Last minute beta backport of rust-lang/rust#151232 ([beta backport accepted](https://rust-lang.zulipchat.com/#narrow/channel/266220-t-rustdoc/topic/beta-nominated.3A.20.23151232/near/568543129)).
Directly fixes rust-lang/rust#149635.

Not part of some hypothetical future backport rollup because we're under time pressure as the release procedures have commenced already: https://forge.rust-lang.org/#current-release-versions, https://forge.rust-lang.org/release/process.html.

r? fmease
2026-01-17 13:03:33 +00:00
León Orell Valerian Liehr
36e6625457
[beta] rustdoc: Stop unconditionally evaluating the initializer of associated consts 2026-01-17 12:25:36 +01:00
rust-bors[bot]
7bea8c6cf1
Auto merge of #150848 - cuviper:beta-next, r=cuviper
[beta] backports

- Revert "Rollup merge of rust-lang/rust#149147 - chenyukang:yukang-fix-unused_assignments-macro-gen-147648, r=JonathanBrouwer" rust-lang/rust#149657
- Don't lint on interior mutable `const` item coming from derefs rust-lang/rust#150166
- stdarch subtree update rust-lang/rust#150639 (partial)
- Update bors configuration rust-lang/rust#150308
- Update bors e-mail lookup rust-lang/rust#150783
- Make verify-channel.sh script compatible with new bors rust-lang/rust#150759

r? cuviper
2026-01-09 18:52:14 +00:00
Jakub Beránek
4dc996fa0f Remove unused environment variable
Its last use was removed in https://github.com/rust-lang/rust/pull/142827.

(cherry picked from commit f2d0d52c2f)
2026-01-09 09:40:31 -08:00
Jakub Beránek
05448c2b8d Update bors e-mail lookup
(cherry picked from commit 138cc27f49)
2026-01-09 09:40:31 -08:00
Jakub Beránek
2827b1b83d Make verify-channel.sh script compatible with new bors
(cherry picked from commit 6f7313e8ad)
2026-01-09 09:40:27 -08:00
Jakub Beránek
af53cfa882 Add support for automation/bors/auto merge branch
(cherry picked from commit bbf4ec3b0f)
2026-01-09 09:40:24 -08:00
Jakub Beránek
efcf90104e Update bors configuration
(cherry picked from commit 75f53dd70e)
2026-01-09 09:40:23 -08:00
usamoi
d851fe6684 partially revert 8d597aa365
(cherry picked from commit 5e4168b162)
2026-01-08 16:20:02 -08:00
Sayantan Chakraborty
c5b220e1de Merge pull request #1985 from usamoi/vpmaddwd
Use LLVM intrinsics for `madd` intrinsics

(cherry picked from commit 85f3ba3dd1)
2026-01-08 16:18:36 -08:00
Urgau
249d399caa Don't lint on interior mutable const item coming from derefs
(cherry picked from commit 2581c2571c)
2026-01-08 16:09:05 -08:00
Urgau
0aa11b5dd2 Add regression test for const_item_interior_mutations deref FP
(cherry picked from commit 0bc29cec45)
2026-01-08 16:09:05 -08:00
Jakub Beránek
9f8bfd6b4a Revert "Rollup merge of #149147 - chenyukang:yukang-fix-unused_assignments-macro-gen-147648, r=JonathanBrouwer"
This reverts commit 82a17b30d8, reversing
changes made to 5019bdaefe.

(cherry picked from commit 74387157c7)
2026-01-08 16:08:18 -08:00
bors
72b6488ba4 Auto merge of #150214 - cuviper:beta-next, r=cuviper
[beta] backports

- attempt to fix unreachable code regression rust-lang/rust#149664
- Update to LLVM 21.1.8 rust-lang/rust#150057
- Revert rust-lang/rust#148937 -- rust-lang/rust#150096

r? cuviper
2025-12-21 04:14:38 +00:00
Amanieu d'Antras
4bc6d75592 Revert #148937 (Remove initialized-bytes tracking from BorrowedBuf and BorrowedCursor)
This caused several performance regressions because of existing code
which uses `Read::read` and therefore requires full buffer
initialization. This is particularly a problem when the same buffer is
re-used for multiple read calls since this means it needs to be fully
re-initialized each time.

There is still some benefit to landing the API changes, but we will have
to add private APIs so that the existing infrastructure can
track and avoid redundant initialization.

(cherry picked from commit 4b07875505)
2025-12-20 13:58:49 -08:00
Nikita Popov
834ced45a4 Update to LLVM 21.1.8
(cherry picked from commit f33abb17db)
2025-12-20 13:54:29 -08:00
Kivooeo
3f4f07f0b9 add check for uninhabited types along side never
(cherry picked from commit 2a2da782d3)
2025-12-20 13:54:03 -08:00
bors
e17ea4bf4e Auto merge of #149951 - BoxyUwU:beta_bump_stage0, r=cuviper
[beta] bump stage0

r? release
2025-12-18 21:10:55 +00:00
bors
6dfb8017ed Auto merge of #149900 - ranger-ross:backport-cargofix, r=weihanglo
[beta-1.93] Backport Cargo bug fix for 1.93

2 commits in 2c283a9a5c5968eeb9a8f12313f04feb1ff8dfac..083ac5135f967fd9dc906ab057a2315861c7a80d
2025-12-04 16:47:28 +0000 to 2025-12-11 23:19:46 -0500
- [beta-1.93] Backport locking fixes (rust-lang/cargo#16386)
- [beta-1.93] Downgrade curl-sys to 0.4.83 (rust-lang/cargo/pull/16399)
2025-12-16 03:12:22 +00:00
Ross Sullivan
2a8e61f538
Backport Cargo bug fixes for Rust 1.93 2025-12-16 11:40:55 +09:00
bors
eb937a3172 Auto merge of #149799 - lnicola:backport-disable-postcard, r=lnicola
[beta] Backport rust-analyzer proc macro server "disable postcard use temporarily"
2025-12-14 18:25:26 +00:00
Boxy Uwu
de868f6b43 bump stage0 2025-12-13 14:31:45 +00:00
Lukas Wirth
51da96d7d2 fix: Disable postcard use temporarily 2025-12-09 08:55:41 +02:00
bors
1b6e21e163 Auto merge of #149760 - BoxyUwU:beta, r=BoxyUwU
[beta] prepare rust 1.93.0

r? `@ghost`
2025-12-08 15:58:14 +00:00
Boxy Uwu
f80e3ac624 bump channel 2025-12-08 10:08:52 +00:00
Boxy Uwu
3c55c98ec8 replace version placeholder 2025-12-08 10:08:32 +00:00
9284 changed files with 169379 additions and 265183 deletions

1
.github/FUNDING.yml vendored
View file

@ -1 +0,0 @@
custom: ["rust-lang.org/funding"]

View file

@ -2,7 +2,6 @@
name: Internal Compiler Error
about: Create a report for an internal compiler error in rustc.
labels: C-bug, I-ICE, T-compiler
title: "[ICE]: "
---
<!--
Thank you for finding an Internal Compiler Error! 🧊 If possible, try to provide

View file

@ -11,9 +11,11 @@ name: CI
on:
push:
branches:
- automation/bors/auto
- automation/bors/try
- auto
- try
- try-perf
- automation/bors/try
- automation/bors/auto
pull_request:
branches:
- "**"
@ -31,10 +33,9 @@ defaults:
concurrency:
# For a given workflow, if we push to the same branch, cancel all previous builds on that branch.
# We add an exception for try builds (automation/bors/try branch) and unrolled rollup builds
# (try-perf), which are all triggered on the same branch, but which should be able to run
# concurrently.
group: ${{ github.workflow }}-${{ ((github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/automation/bors/try') && github.sha) || github.ref }}
# We add an exception for try builds (try branch) and unrolled rollup builds (try-perf), which
# are all triggered on the same branch, but which should be able to run concurrently.
group: ${{ github.workflow }}-${{ ((github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/automation/bors/try') && github.sha) || github.ref }}
cancel-in-progress: true
env:
TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate"
@ -56,7 +57,7 @@ jobs:
- name: Test citool
# Only test citool on the auto branch, to reduce latency of the calculate matrix job
# on PR/try builds.
if: ${{ github.ref == 'refs/heads/automation/bors/auto' }}
if: ${{ github.ref == 'refs/heads/auto' || github.ref == 'refs/heads/automation/bors/auto' }}
run: |
cd src/ci/citool
CARGO_INCREMENTAL=0 cargo test
@ -79,7 +80,7 @@ jobs:
# access the environment.
#
# We only enable the environment for the rust-lang/rust repository, so that CI works on forks.
environment: ${{ ((github.repository == 'rust-lang/rust' && (github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/automation/bors/try' || github.ref == 'refs/heads/automation/bors/auto')) && 'bors') || '' }}
environment: ${{ ((github.repository == 'rust-lang/rust' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/automation/bors/try' || github.ref == 'refs/heads/auto' || github.ref == 'refs/heads/automation/bors/auto')) && 'bors') || '' }}
env:
CI_JOB_NAME: ${{ matrix.name }}
CI_JOB_DOC_URL: ${{ matrix.doc_url }}
@ -165,6 +166,9 @@ jobs:
- name: install sccache
run: src/ci/scripts/install-sccache.sh
- name: select Xcode
run: src/ci/scripts/select-xcode.sh
- name: install clang
run: src/ci/scripts/install-clang.sh
@ -302,22 +306,30 @@ jobs:
DD_GITHUB_JOB_NAME: ${{ matrix.full_name }}
run: ./build/citool/debug/citool upload-build-metrics build/cpu-usage.csv
# This job is used to publish toolstate for successful auto builds.
# This job isused to tell bors the final status of the build, as there is no practical way to detect
# when a workflow is successful listening to webhooks only in our current bors implementation (homu).
outcome:
name: publish toolstate
name: bors build finished
runs-on: ubuntu-24.04
needs: [ calculate_matrix, job ]
if: ${{ needs.calculate_matrix.outputs.run_type == 'auto' }}
environment: ${{ (github.repository == 'rust-lang/rust' && 'bors') || '' }}
# !cancelled() executes the job regardless of whether the previous jobs passed or failed
if: ${{ !cancelled() && contains(fromJSON('["auto", "try"]'), needs.calculate_matrix.outputs.run_type) }}
environment: ${{ ((github.repository == 'rust-lang/rust' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf' || github.ref == 'refs/heads/automation/bors/try' || github.ref == 'refs/heads/auto' || github.ref == 'refs/heads/automation/bors/auto')) && 'bors') || '' }}
steps:
- name: checkout the source code
uses: actions/checkout@v5
with:
fetch-depth: 2
# Calculate the exit status of the whole CI workflow.
# If all dependent jobs were successful, this exits with 0 (and the outcome job continues successfully).
# If a some dependent job has failed, this exits with 1.
- name: calculate the correct exit status
run: jq --exit-status 'all(.result == "success" or .result == "skipped")' <<< '${{ toJson(needs) }}'
# Publish the toolstate if an auto build succeeds (just before push to the default branch)
- name: publish toolstate
run: src/ci/publish_toolstate.sh
shell: bash
if: needs.calculate_matrix.outputs.run_type == 'auto'
env:
TOOLSTATE_ISSUES_API_URL: https://api.github.com/repos/rust-lang/rust/issues
TOOLSTATE_PUBLISH: 1

View file

@ -62,9 +62,19 @@ jobs:
rustup toolchain install --no-self-update --profile minimal $TOOLCHAIN
rustup default $TOOLCHAIN
- name: cargo update
run: ./src/tools/update-lockfile.sh
- name: cargo update compiler & tools
# Remove first line that always just says "Updating crates.io index"
run: |
echo -e "\ncompiler & tools dependencies:" >> cargo_update.log
cargo update 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
- name: cargo update library
run: |
echo -e "\nlibrary dependencies:" >> cargo_update.log
cargo update --manifest-path library/Cargo.toml 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
- name: cargo update rustbook
run: |
echo -e "\nrustbook dependencies:" >> cargo_update.log
cargo update --manifest-path src/tools/rustbook/Cargo.toml 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
- name: upload Cargo.lock artifact for use in PR
uses: actions/upload-artifact@v4
with:

View file

@ -55,8 +55,6 @@ jobs:
images=(
# Mirrored because used by the tidy job, which doesn't cache Docker images
"ubuntu:22.04"
# Mirrored because used by x86-64-gnu-miri
"ubuntu:24.04"
# Mirrored because used by all linux CI jobs, including tidy
"moby/buildkit:buildx-stable-1"
# Mirrored because used when CI is running inside a Docker container

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/22.1-2026-01-27
branch = rustc/21.1-2025-08-01
shallow = true
[submodule "src/doc/embedded-book"]
path = src/doc/embedded-book

View file

@ -83,7 +83,6 @@ Ben Sago <ogham@users.noreply.github.com> <ogham@bsago.me>
Ben Striegel <ben.striegel@gmail.com>
Benjamin Jackman <ben@jackman.biz>
Benoît Cortier <benoit.cortier@fried-world.eu>
binarycat <binarycat@envs.net> lolbinarycat <dogedoge61+github@gmail.com> <dogedoge61@gmail.com>
Bheesham Persaud <bheesham123@hotmail.com> Bheesham Persaud <bheesham.persaud@live.ca>
bjorn3 <17426603+bjorn3@users.noreply.github.com> <bjorn3@users.noreply.github.com>
bjorn3 <17426603+bjorn3@users.noreply.github.com> <bjorn3_gh@protonmail.com>
@ -96,7 +95,6 @@ boolean_coercion <booleancoercion@gmail.com>
Boris Egorov <jightuse@gmail.com> <egorov@linux.com>
bors <bors@rust-lang.org> bors[bot] <26634292+bors[bot]@users.noreply.github.com>
bors <bors@rust-lang.org> bors[bot] <bors[bot]@users.noreply.github.com>
bors <bors@rust-lang.org> <122020455+rust-bors[bot]@users.noreply.github.com>
BoxyUwU <rust@boxyuwu.dev>
BoxyUwU <rust@boxyuwu.dev> <supbscripter@gmail.com>
Braden Nelson <moonheart08@users.noreply.github.com>
@ -432,7 +430,6 @@ Lzu Tao <taolzu@gmail.com>
Maik Klein <maikklein@googlemail.com>
Maja Kądziołka <maya@compilercrim.es> <github@compilercrim.es>
Maja Kądziołka <maya@compilercrim.es> <kuba@kadziolka.net>
Makai <m4kai410@gmail.com>
Malo Jaffré <jaffre.malo@gmail.com>
Manish Goregaokar <manishsmail@gmail.com>
Mara Bos <m-ou.se@m-ou.se>

View file

@ -10,7 +10,7 @@ the Zulip stream is the best place to *ask* for help.
Documentation for contributing to the compiler or tooling is located in the [Guide to Rustc
Development][rustc-dev-guide], commonly known as the [rustc-dev-guide]. Documentation for the
standard library is in the [Standard library developers Guide][std-dev-guide], commonly known as the [std-dev-guide].
standard library in the [Standard library developers Guide][std-dev-guide], commonly known as the [std-dev-guide].
## Making changes to subtrees and submodules

File diff suppressed because it is too large Load diff

View file

@ -97,7 +97,7 @@ See [the rustc-dev-guide for more info][sysllvm].
--set llvm.ninja=false \
--set rust.debug-assertions=false \
--set rust.jemalloc \
--set rust.bootstrap-override-lld=true \
--set rust.use-lld=true \
--set rust.lto=thin \
--set rust.codegen-units=1
```
@ -233,7 +233,7 @@ itself back on after some time).
### MSVC
MSVC builds of Rust additionally require an installation of:
MSVC builds of Rust additionally requires an installation of:
- Visual Studio 2022 (or later) build tools so `rustc` can use its linker. Older
Visual Studio versions such as 2019 *may* work but aren't actively tested.

View file

@ -1555,7 +1555,7 @@ Compatibility Notes
- [Check well-formedness of the source type's signature in fn pointer casts.](https://github.com/rust-lang/rust/pull/129021) This partly closes a soundness hole that comes when casting a function item to function pointer
- [Use equality instead of subtyping when resolving type dependent paths.](https://github.com/rust-lang/rust/pull/129073)
- Linking on macOS now correctly includes Rust's default deployment target. Due to a linker bug, you might have to pass `MACOSX_DEPLOYMENT_TARGET` or fix your `#[link]` attributes to point to the correct frameworks. See <https://github.com/rust-lang/rust/pull/129369>.
- [Rust will now correctly raise an error for `repr(Rust)` written on non-`struct`/`enum`/`union` items, since it previously did not have any effect.](https://github.com/rust-lang/rust/pull/129422)
- [Rust will now correctly raise an error for `repr(Rust)` written on non-`struct`/`enum`/`union` items, since it previous did not have any effect.](https://github.com/rust-lang/rust/pull/129422)
- The future incompatibility lint `deprecated_cfg_attr_crate_type_name` [has been made into a hard error](https://github.com/rust-lang/rust/pull/129670). It was used to deny usage of `#![crate_type]` and `#![crate_name]` attributes in `#![cfg_attr]`, which required a hack in the compiler to be able to change the used crate type and crate name after cfg expansion.
Users can use `--crate-type` instead of `#![cfg_attr(..., crate_type = "...")]` and `--crate-name` instead of `#![cfg_attr(..., crate_name = "...")]` when running `rustc`/`cargo rustc` on the command line.
Use of those two attributes outside of `#![cfg_attr]` continue to be fully supported.
@ -1731,7 +1731,7 @@ Cargo
Compatibility Notes
-------------------
- We now [disallow setting some built-in cfgs via the command-line](https://github.com/rust-lang/rust/pull/126158) with the newly added [`explicit_builtin_cfgs_in_flags`](https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#explicit-builtin-cfgs-in-flags) lint in order to prevent incoherent state, eg. `windows` cfg active but target is Linux based. The appropriate [`rustc` flag](https://doc.rust-lang.org/rustc/command-line-arguments.html) should be used instead.
- The standard library has a new implementation of `binary_search` which significantly improves performance ([#128254](https://github.com/rust-lang/rust/pull/128254)). However when a sorted slice has multiple values which compare equal, the new implementation may select a different value among the equal ones than the old implementation.
- The standard library has a new implementation of `binary_search` which is significantly improves performance ([#128254](https://github.com/rust-lang/rust/pull/128254)). However when a sorted slice has multiple values which compare equal, the new implementation may select a different value among the equal ones than the old implementation.
- [illumos/Solaris now sets `MSG_NOSIGNAL` when writing to sockets](https://github.com/rust-lang/rust/pull/128259). This avoids killing the process with SIGPIPE when writing to a closed socket, which matches the existing behavior on other UNIX targets.
- [Removes a problematic hack that always passed the --whole-archive linker flag for tests, which may cause linker errors for code accidentally relying on it.](https://github.com/rust-lang/rust/pull/128400)
- The WebAssembly target features `multivalue` and `reference-types` are now
@ -1881,7 +1881,7 @@ 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.
- [Add a Rust-for-Linux `auto` CI job to check kernel builds.](https://github.com/rust-lang/rust/pull/125209/)
- [Add a Rust-for Linux `auto` CI job to check kernel builds.](https://github.com/rust-lang/rust/pull/125209/)
Version 1.80.1 (2024-08-08)
===========================
@ -4519,7 +4519,7 @@ Compatibility Notes
saturating to `0` instead][89926]. In the real world the panic happened mostly
on platforms with buggy monotonic clock implementations rather than catching
programming errors like reversing the start and end times. Such programming
errors will now result in `0` rather than a panic.
errors will now results in `0` rather than a panic.
- In a future release we're planning to increase the baseline requirements for
the Linux kernel to version 3.2, and for glibc to version 2.17. We'd love
your feedback in [PR #95026][95026].

View file

@ -53,7 +53,7 @@ path = [
]
precedence = "override"
SPDX-FileCopyrightText = "The Rust Project Developers (see https://thanks.rust-lang.org)"
SPDX-License-Identifier = "MIT OR Apache-2.0"
SPDX-License-Identifier = "MIT or Apache-2.0"
[[annotations]]
path = "compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp"

View file

@ -106,9 +106,6 @@
# Whether to build LLVM with support for it's gpu offload runtime.
#llvm.offload = false
# Absolute path to the directory containing ClangConfig.cmake
#llvm.offload-clang-dir = ""
# When true, link libstdc++ statically into the rustc_llvm.
# This is useful if you don't want to use the dynamic version of that
# library provided by LLVM.
@ -194,31 +191,6 @@
# Currently, this is only supported for the `x86_64-unknown-linux-gnu` target.
#gcc.download-ci-gcc = false
# Provide a directory of prebuilt libgccjit.so dylibs for given (host, target) compilation pairs.
# This is useful when you want to cross-compile `rustc` to another target since GCC is not a
# multi-target compiler.
# You have to use a directory structure that looks like this:
# `<libgccjit-libs-dir>/<host>/<target>/libgccjit.so`.
# For example:
#
# ```
# <libgccjit-libs-dir>
# ├── m68k-unknown-linux-gnu
# │ └── m68k-unknown-linux-gnu
# │ └── libgccjit.so
# └── x86_64-unknown-linux-gnu
# ├── m68k-unknown-linux-gnu
# │ └── libgccjit.so
# └── x86_64-unknown-linux-gnu
# └── libgccjit.so
# ```
# The directory above would allow you to cross-compile rustc from x64 to m68k
#
# Note that this option has priority over `gcc.download-ci-gcc`.
# If you set both, bootstrap will first try to load libgccjit.so from this directory.
# Only if it isn't found, it will try to download it from CI or build it locally.
#gcc.libgccjit-libs-dir = "/path/to/libgccjit-libs-dir"
# =============================================================================
# General build configuration options
# =============================================================================

View file

@ -3,8 +3,6 @@
// Several crates are depended upon but unused so that they are present in the sysroot
#![expect(unused_crate_dependencies)]
use std::process::ExitCode;
// A note about jemalloc: rustc uses jemalloc when built for CI and
// distribution. The obvious way to do this is with the `#[global_allocator]`
// mechanism. However, for complicated reasons (see
@ -40,6 +38,6 @@ use std::process::ExitCode;
#[cfg(feature = "jemalloc")]
use tikv_jemalloc_sys as _;
fn main() -> ExitCode {
fn main() {
rustc_driver::main()
}

View file

@ -60,7 +60,6 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
/// This is public so that it can be used in unit tests, but
/// should generally only be relevant to the ABI details of
/// specific targets.
#[tracing::instrument(skip(cx), level = "debug")]
pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous>
where
Ty: TyAbiInterface<'a, C> + Copy,
@ -83,10 +82,6 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
}))
}
BackendRepr::ScalableVector { .. } => {
unreachable!("`homogeneous_aggregate` should not be called for scalable vectors")
}
BackendRepr::ScalarPair(..) | BackendRepr::Memory { sized: true } => {
// Helper for computing `homogeneous_aggregate`, allowing a custom
// starting offset (used below for handling variants).

View file

@ -35,7 +35,6 @@ impl Reg {
reg_ctor!(f32, Float, 32);
reg_ctor!(f64, Float, 64);
reg_ctor!(f128, Float, 128);
}
impl Reg {

View file

@ -27,7 +27,6 @@ pub enum CanonAbi {
C,
Rust,
RustCold,
RustPreserveNone,
/// An ABI that rustc does not know how to call or define.
Custom,
@ -55,7 +54,7 @@ pub enum CanonAbi {
impl CanonAbi {
pub fn is_rustic_abi(self) -> bool {
match self {
CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::RustPreserveNone => true,
CanonAbi::Rust | CanonAbi::RustCold => true,
CanonAbi::C
| CanonAbi::Custom
| CanonAbi::Arm(_)
@ -75,7 +74,6 @@ impl fmt::Display for CanonAbi {
CanonAbi::C => ExternAbi::C { unwind: false },
CanonAbi::Rust => ExternAbi::Rust,
CanonAbi::RustCold => ExternAbi::RustCold,
CanonAbi::RustPreserveNone => ExternAbi::RustPreserveNone,
CanonAbi::Custom => ExternAbi::Custom,
CanonAbi::Arm(arm_call) => match arm_call {
ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },

View file

@ -42,13 +42,6 @@ pub enum ExternAbi {
/// in a platform-agnostic way.
RustInvalid,
/// Preserves no registers.
///
/// Note, that this ABI is not stable in the registers it uses, is intended as an optimization
/// and may fall-back to a more conservative calling convention if the backend does not support
/// forcing callers to save all registers.
RustPreserveNone,
/// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM.
/// Even normally-compatible Rust types can become ABI-incompatible with this ABI!
Unadjusted,
@ -74,6 +67,7 @@ pub enum ExternAbi {
/* gpu */
/// An entry-point function called by the GPU's host
// FIXME: should not be callable from Rust on GPU targets, is for host's use only
GpuKernel,
/// An entry-point function called by the GPU's host
// FIXME: why do we have two of these?
@ -170,7 +164,6 @@ abi_impls! {
RustCall =><= "rust-call",
RustCold =><= "rust-cold",
RustInvalid =><= "rust-invalid",
RustPreserveNone =><= "rust-preserve-none",
Stdcall { unwind: false } =><= "stdcall",
Stdcall { unwind: true } =><= "stdcall-unwind",
System { unwind: false } =><= "system",
@ -251,7 +244,7 @@ impl ExternAbi {
/// - are subject to change between compiler versions
pub fn is_rustic_abi(self) -> bool {
use ExternAbi::*;
matches!(self, Rust | RustCall | RustCold | RustPreserveNone)
matches!(self, Rust | RustCall | RustCold)
}
/// Returns whether the ABI supports C variadics. This only controls whether we allow *imports*
@ -323,8 +316,7 @@ impl ExternAbi {
| Self::Thiscall { .. }
| Self::Vectorcall { .. }
| Self::SysV64 { .. }
| Self::Win64 { .. }
| Self::RustPreserveNone => true,
| Self::Win64 { .. } => true,
}
}
}

View file

@ -1,7 +1,6 @@
use std::assert_matches::assert_matches;
use std::str::FromStr;
use rustc_data_structures::assert_matches;
use super::*;
#[allow(non_snake_case)]

View file

@ -11,7 +11,7 @@ use tracing::{debug, trace};
use crate::{
AbiAlign, Align, BackendRepr, FieldsShape, HasDataLayout, IndexSlice, IndexVec, Integer,
LayoutData, Niche, NonZeroUsize, Primitive, ReprOptions, Scalar, Size, StructKind, TagEncoding,
TargetDataLayout, Variants, WrappingRange,
Variants, WrappingRange,
};
mod coroutine;
@ -143,32 +143,58 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
})
}
pub fn scalable_vector_type<FieldIdx, VariantIdx, F>(
&self,
element: F,
count: u64,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F>
where
pub fn simd_type<
FieldIdx: Idx,
VariantIdx: Idx,
F: AsRef<LayoutData<FieldIdx, VariantIdx>> + fmt::Debug,
{
vector_type_layout(VectorKind::Scalable, self.cx.data_layout(), element, count)
}
pub fn simd_type<FieldIdx, VariantIdx, F>(
>(
&self,
element: F,
count: u64,
repr_packed: bool,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F>
where
FieldIdx: Idx,
VariantIdx: Idx,
F: AsRef<LayoutData<FieldIdx, VariantIdx>> + fmt::Debug,
{
let kind = if repr_packed { VectorKind::PackedFixed } else { VectorKind::Fixed };
vector_type_layout(kind, self.cx.data_layout(), element, count)
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
let elt = element.as_ref();
if count == 0 {
return Err(LayoutCalculatorError::ZeroLengthSimdType);
} else if count > crate::MAX_SIMD_LANES {
return Err(LayoutCalculatorError::OversizedSimdType {
max_lanes: crate::MAX_SIMD_LANES,
});
}
let BackendRepr::Scalar(e_repr) = elt.backend_repr else {
return Err(LayoutCalculatorError::NonPrimitiveSimdType(element));
};
// Compute the size and alignment of the vector
let dl = self.cx.data_layout();
let size =
elt.size.checked_mul(count, dl).ok_or_else(|| LayoutCalculatorError::SizeOverflow)?;
let (repr, align) = if repr_packed && !count.is_power_of_two() {
// 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 }, Align::max_aligned_factor(size))
} else {
(BackendRepr::SimdVector { element: e_repr, count }, dl.llvmlike_vector_align(size))
};
let size = size.align_to(align);
Ok(LayoutData {
variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldsShape::Arbitrary {
offsets: [Size::ZERO].into(),
memory_index: [0].into(),
},
backend_repr: repr,
largest_niche: elt.largest_niche,
uninhabited: false,
size,
align: AbiAlign::new(align),
max_repr_align: None,
unadjusted_abi_align: elt.align.abi,
randomization_seed: elt.randomization_seed.wrapping_add(Hash64::new(count)),
})
}
/// Compute the layout for a coroutine.
@ -427,7 +453,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
BackendRepr::Scalar(..)
| BackendRepr::ScalarPair(..)
| BackendRepr::SimdVector { .. }
| BackendRepr::ScalableVector { .. }
| BackendRepr::Memory { .. } => repr,
},
};
@ -499,8 +524,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
hide_niches(a);
hide_niches(b);
}
BackendRepr::SimdVector { element, .. }
| BackendRepr::ScalableVector { element, .. } => hide_niches(element),
BackendRepr::SimdVector { element, count: _ } => hide_niches(element),
BackendRepr::Memory { sized: _ } => {}
}
st.largest_niche = None;
@ -714,7 +738,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
},
fields: FieldsShape::Arbitrary {
offsets: [niche_offset].into(),
in_memory_order: [FieldIdx::new(0)].into(),
memory_index: [0].into(),
},
backend_repr: abi,
largest_niche,
@ -1008,8 +1032,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
let pair =
LayoutData::<FieldIdx, VariantIdx>::scalar_pair(&self.cx, tag, prim_scalar);
let pair_offsets = match pair.fields {
FieldsShape::Arbitrary { ref offsets, ref in_memory_order } => {
assert_eq!(in_memory_order.raw, [FieldIdx::new(0), FieldIdx::new(1)]);
FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
assert_eq!(memory_index.raw, [0, 1]);
offsets
}
_ => panic!("encountered a non-arbitrary layout during enum layout"),
@ -1061,7 +1085,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
},
fields: FieldsShape::Arbitrary {
offsets: [Size::ZERO].into(),
in_memory_order: [FieldIdx::new(0)].into(),
memory_index: [0].into(),
},
largest_niche,
uninhabited,
@ -1110,10 +1134,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
let pack = repr.pack;
let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align };
let mut max_repr_align = repr.align;
let mut in_memory_order: IndexVec<u32, FieldIdx> = fields.indices().collect();
let mut inverse_memory_index: IndexVec<u32, FieldIdx> = fields.indices().collect();
let optimize_field_order = !repr.inhibit_struct_field_reordering();
let end = if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() };
let optimizing = &mut in_memory_order.raw[..end];
let optimizing = &mut inverse_memory_index.raw[..end];
let fields_excluding_tail = &fields.raw[..end];
// unsizable tail fields are excluded so that we use the same seed for the sized and unsized layouts.
let field_seed = fields_excluding_tail
@ -1248,10 +1272,12 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
// regardless of the status of `-Z randomize-layout`
}
}
// in_memory_order holds field indices by increasing memory offset.
// That is, if field 5 has offset 0, the first element of in_memory_order is 5.
// inverse_memory_index holds field indices by increasing memory offset.
// That is, if field 5 has offset 0, the first element of inverse_memory_index is 5.
// We now write field offsets to the corresponding offset slot;
// field 5 with offset 0 puts 0 in offsets[5].
// At the bottom of this function, we invert `inverse_memory_index` to
// produce `memory_index` (see `invert_mapping`).
let mut unsized_field = None::<&F>;
let mut offsets = IndexVec::from_elem(Size::ZERO, fields);
let mut offset = Size::ZERO;
@ -1263,7 +1289,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
align = align.max(prefix_align);
offset = prefix_size.align_to(prefix_align);
}
for &i in &in_memory_order {
for &i in &inverse_memory_index {
let field = &fields[i];
if let Some(unsized_field) = unsized_field {
return Err(LayoutCalculatorError::UnexpectedUnsized(*unsized_field));
@ -1320,6 +1346,18 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
debug!("univariant min_size: {:?}", offset);
let min_size = offset;
// As stated above, inverse_memory_index holds field indices by increasing offset.
// This makes it an already-sorted view of the offsets vec.
// To invert it, consider:
// If field 5 has offset 0, offsets[0] is 5, and memory_index[5] should be 0.
// Field 5 would be the first element, so memory_index is i:
// Note: if we didn't optimize, it's already right.
let memory_index = if optimize_field_order {
inverse_memory_index.invert_bijective_mapping()
} else {
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);
// FIXME(oli-obk): deduplicate and harden these checks
if size.bytes() >= dl.obj_size_bound() {
@ -1375,11 +1413,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
let pair =
LayoutData::<FieldIdx, VariantIdx>::scalar_pair(&self.cx, a, b);
let pair_offsets = match pair.fields {
FieldsShape::Arbitrary { ref offsets, ref in_memory_order } => {
assert_eq!(
in_memory_order.raw,
[FieldIdx::new(0), FieldIdx::new(1)]
);
FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
assert_eq!(memory_index.raw, [0, 1]);
offsets
}
FieldsShape::Primitive
@ -1423,7 +1458,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
Ok(LayoutData {
variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldsShape::Arbitrary { offsets, in_memory_order },
fields: FieldsShape::Arbitrary { offsets, memory_index },
backend_repr: abi,
largest_niche,
uninhabited,
@ -1466,70 +1501,3 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
s
}
}
enum VectorKind {
/// `#[rustc_scalable_vector]`
Scalable,
/// `#[repr(simd, packed)]`
PackedFixed,
/// `#[repr(simd)]`
Fixed,
}
fn vector_type_layout<FieldIdx, VariantIdx, F>(
kind: VectorKind,
dl: &TargetDataLayout,
element: F,
count: u64,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F>
where
FieldIdx: Idx,
VariantIdx: Idx,
F: AsRef<LayoutData<FieldIdx, VariantIdx>> + fmt::Debug,
{
let elt = element.as_ref();
if count == 0 {
return Err(LayoutCalculatorError::ZeroLengthSimdType);
} else if count > crate::MAX_SIMD_LANES {
return Err(LayoutCalculatorError::OversizedSimdType { max_lanes: crate::MAX_SIMD_LANES });
}
let BackendRepr::Scalar(element) = elt.backend_repr else {
return Err(LayoutCalculatorError::NonPrimitiveSimdType(element));
};
// Compute the size and alignment of the vector
let size =
elt.size.checked_mul(count, dl).ok_or_else(|| LayoutCalculatorError::SizeOverflow)?;
let (repr, align) = match kind {
VectorKind::Scalable => {
(BackendRepr::ScalableVector { element, count }, dl.llvmlike_vector_align(size))
}
// 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.
VectorKind::PackedFixed if !count.is_power_of_two() => {
(BackendRepr::Memory { sized: true }, Align::max_aligned_factor(size))
}
VectorKind::PackedFixed | VectorKind::Fixed => {
(BackendRepr::SimdVector { element, count }, dl.llvmlike_vector_align(size))
}
};
let size = size.align_to(align);
Ok(LayoutData {
variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldsShape::Arbitrary {
offsets: [Size::ZERO].into(),
in_memory_order: [FieldIdx::new(0)].into(),
},
backend_repr: repr,
largest_niche: elt.largest_niche,
uninhabited: false,
size,
align: AbiAlign::new(align),
max_repr_align: None,
unadjusted_abi_align: elt.align.abi,
randomization_seed: elt.randomization_seed.wrapping_add(Hash64::new(count)),
})
}

View file

@ -182,29 +182,33 @@ pub(super) fn layout<
// CoroutineLayout.
debug!("prefix = {:#?}", prefix);
let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields {
FieldsShape::Arbitrary { mut offsets, in_memory_order } => {
FieldsShape::Arbitrary { mut offsets, memory_index } => {
let mut inverse_memory_index = memory_index.invert_bijective_mapping();
// "a" (`0..b_start`) and "b" (`b_start..`) correspond to
// "outer" and "promoted" fields respectively.
let b_start = tag_index.plus(1);
let offsets_b = IndexVec::from_raw(offsets.raw.split_off(b_start.index()));
let offsets_a = offsets;
// Disentangle the "a" and "b" components of `in_memory_order`
// Disentangle the "a" and "b" components of `inverse_memory_index`
// by preserving the order but keeping only one disjoint "half" each.
// FIXME(eddyb) build a better abstraction for permutations, if possible.
let mut in_memory_order_a = IndexVec::<u32, FieldIdx>::new();
let mut in_memory_order_b = IndexVec::<u32, FieldIdx>::new();
for i in in_memory_order {
if let Some(j) = i.index().checked_sub(b_start.index()) {
in_memory_order_b.push(FieldIdx::new(j));
} else {
in_memory_order_a.push(i);
}
}
let inverse_memory_index_b: IndexVec<u32, FieldIdx> = inverse_memory_index
.iter()
.filter_map(|&i| i.index().checked_sub(b_start.index()).map(FieldIdx::new))
.collect();
inverse_memory_index.raw.retain(|&i| i.index() < b_start.index());
let inverse_memory_index_a = inverse_memory_index;
// Since `inverse_memory_index_{a,b}` each only refer to their
// respective fields, they can be safely inverted
let memory_index_a = inverse_memory_index_a.invert_bijective_mapping();
let memory_index_b = inverse_memory_index_b.invert_bijective_mapping();
let outer_fields =
FieldsShape::Arbitrary { offsets: offsets_a, in_memory_order: in_memory_order_a };
(outer_fields, offsets_b, in_memory_order_b.invert_bijective_mapping())
FieldsShape::Arbitrary { offsets: offsets_a, memory_index: memory_index_a };
(outer_fields, offsets_b, memory_index_b)
}
_ => unreachable!(),
};
@ -232,7 +236,7 @@ pub(super) fn layout<
)?;
variant.variants = Variants::Single { index };
let FieldsShape::Arbitrary { offsets, in_memory_order } = variant.fields else {
let FieldsShape::Arbitrary { offsets, memory_index } = variant.fields else {
unreachable!();
};
@ -245,9 +249,8 @@ pub(super) fn layout<
// promoted fields were being used, but leave the elements not in the
// subset as `invalid_field_idx`, which we can filter out later to
// obtain a valid (bijective) mapping.
let memory_index = in_memory_order.invert_bijective_mapping();
let invalid_field_idx = promoted_memory_index.len() + memory_index.len();
let mut combined_in_memory_order =
let mut combined_inverse_memory_index =
IndexVec::from_elem_n(FieldIdx::new(invalid_field_idx), invalid_field_idx);
let mut offsets_and_memory_index = iter::zip(offsets, memory_index);
@ -265,18 +268,19 @@ pub(super) fn layout<
(promoted_offsets[field_idx], promoted_memory_index[field_idx])
}
};
combined_in_memory_order[memory_index] = i;
combined_inverse_memory_index[memory_index] = i;
offset
})
.collect();
// Remove the unused slots to obtain the combined `in_memory_order`
// (also see previous comment).
combined_in_memory_order.raw.retain(|&i| i.index() != invalid_field_idx);
// Remove the unused slots and invert the mapping to obtain the
// combined `memory_index` (also see previous comment).
combined_inverse_memory_index.raw.retain(|&i| i.index() != invalid_field_idx);
let combined_memory_index = combined_inverse_memory_index.invert_bijective_mapping();
variant.fields = FieldsShape::Arbitrary {
offsets: combined_offsets,
in_memory_order: combined_in_memory_order,
memory_index: combined_memory_index,
};
size = size.max(variant.size);

View file

@ -16,7 +16,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldsShape::Arbitrary {
offsets: IndexVec::new(),
in_memory_order: IndexVec::new(),
memory_index: IndexVec::new(),
},
backend_repr: BackendRepr::Memory { sized },
largest_niche: None,
@ -108,7 +108,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldsShape::Arbitrary {
offsets: [Size::ZERO, b_offset].into(),
in_memory_order: [FieldIdx::new(0), FieldIdx::new(1)].into(),
memory_index: [0, 1].into(),
},
backend_repr: BackendRepr::ScalarPair(a, b),
largest_niche,
@ -133,7 +133,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
Some(fields) => FieldsShape::Union(fields),
None => FieldsShape::Arbitrary {
offsets: IndexVec::new(),
in_memory_order: IndexVec::new(),
memory_index: IndexVec::new(),
},
},
backend_repr: BackendRepr::Memory { sized: true },

View file

@ -155,7 +155,7 @@ impl<'a, Ty> AsRef<LayoutData<FieldIdx, VariantIdx>> for TyAndLayout<'a, Ty> {
/// Trait that needs to be implemented by the higher-level type representation
/// (e.g. `rustc_middle::ty::Ty`), to provide `rustc_target::abi` functionality.
pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug + std::fmt::Display {
pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug {
fn ty_and_layout_for_variant(
this: TyAndLayout<'a, Self>,
cx: &C,
@ -172,7 +172,6 @@ pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug + std::fmt::Display {
fn is_tuple(this: TyAndLayout<'a, Self>) -> bool;
fn is_unit(this: TyAndLayout<'a, Self>) -> bool;
fn is_transparent(this: TyAndLayout<'a, Self>) -> bool;
fn is_scalable_vector(this: TyAndLayout<'a, Self>) -> bool;
/// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details.
fn is_pass_indirectly_in_non_rustic_abis_flag_set(this: TyAndLayout<'a, Self>) -> bool;
}
@ -272,13 +271,6 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
Ty::is_transparent(self)
}
pub fn is_scalable_vector<C>(self) -> bool
where
Ty: TyAbiInterface<'a, C>,
{
Ty::is_scalable_vector(self)
}
/// If this method returns `true`, then this type should always have a `PassMode` of
/// `Indirect { on_stack: false, .. }` when being used as the argument type of a function with a
/// non-Rustic ABI (this is true for structs annotated with the
@ -290,19 +282,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
/// function call isn't allowed (a.k.a. `va_list`).
///
/// This function handles transparent types automatically.
pub fn pass_indirectly_in_non_rustic_abis<C>(self, cx: &C) -> bool
where
Ty: TyAbiInterface<'a, C> + Copy,
{
let base = self.peel_transparent_wrappers(cx);
Ty::is_pass_indirectly_in_non_rustic_abis_flag_set(base)
}
/// Recursively peel away transparent wrappers, returning the inner value.
///
/// The return value is not `repr(transparent)` and/or does
/// not have a non-1zst field.
pub fn peel_transparent_wrappers<C>(mut self, cx: &C) -> Self
pub fn pass_indirectly_in_non_rustic_abis<C>(mut self, cx: &C) -> bool
where
Ty: TyAbiInterface<'a, C> + Copy,
{
@ -312,7 +292,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
self = field;
}
self
Ty::is_pass_indirectly_in_non_rustic_abis_flag_set(self)
}
/// Finds the one field that is not a 1-ZST.

View file

@ -1,6 +1,6 @@
// tidy-alphabetical-start
#![cfg_attr(all(feature = "nightly", bootstrap, test), feature(assert_matches))]
#![cfg_attr(feature = "nightly", allow(internal_features))]
#![cfg_attr(feature = "nightly", feature(assert_matches))]
#![cfg_attr(feature = "nightly", feature(rustc_attrs))]
#![cfg_attr(feature = "nightly", feature(step_trait))]
// tidy-alphabetical-end
@ -96,11 +96,9 @@ bitflags! {
/// If true, the type is always passed indirectly by non-Rustic ABIs.
/// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details.
const PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS = 1 << 5;
const IS_SCALABLE = 1 << 6;
// Any of these flags being set prevent field reordering optimisation.
/// Any of these flags being set prevent field reordering optimisation.
const FIELD_ORDER_UNOPTIMIZABLE = ReprFlags::IS_C.bits()
| ReprFlags::IS_SIMD.bits()
| ReprFlags::IS_SCALABLE.bits()
| ReprFlags::IS_LINEAR.bits();
const ABI_UNOPTIMIZABLE = ReprFlags::IS_C.bits() | ReprFlags::IS_SIMD.bits();
}
@ -137,19 +135,6 @@ impl IntegerType {
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, HashStable_Generic)
)]
pub enum ScalableElt {
/// `N` in `rustc_scalable_vector(N)` - the element count of the scalable vector
ElementCount(u16),
/// `rustc_scalable_vector` w/out `N`, used for tuple types of scalable vectors that only
/// contain other scalable vectors
Container,
}
/// Represents the repr options provided by the user.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
#[cfg_attr(
@ -161,8 +146,6 @@ pub struct ReprOptions {
pub align: Option<Align>,
pub pack: Option<Align>,
pub flags: ReprFlags,
/// `#[rustc_scalable_vector]`
pub scalable: Option<ScalableElt>,
/// The seed to be used for randomizing a type's layout
///
/// Note: This could technically be a `u128` which would
@ -179,11 +162,6 @@ impl ReprOptions {
self.flags.contains(ReprFlags::IS_SIMD)
}
#[inline]
pub fn scalable(&self) -> bool {
self.flags.contains(ReprFlags::IS_SCALABLE)
}
#[inline]
pub fn c(&self) -> bool {
self.flags.contains(ReprFlags::IS_C)
@ -1636,14 +1614,19 @@ pub enum FieldsShape<FieldIdx: Idx> {
// FIXME(eddyb) use small vector optimization for the common case.
offsets: IndexVec<FieldIdx, Size>,
/// Maps memory order field indices to source order indices,
/// Maps source order field indices to memory order indices,
/// depending on how the fields were reordered (if at all).
/// This is a permutation, with both the source order and the
/// memory order using the same (0..n) index ranges.
///
/// Note that during computation of `memory_index`, sometimes
/// it is easier to operate on the inverse mapping (that is,
/// from memory order to source order), and that is usually
/// named `inverse_memory_index`.
///
// FIXME(eddyb) build a better abstraction for permutations, if possible.
// FIXME(camlorn) also consider small vector optimization here.
in_memory_order: IndexVec<u32, FieldIdx>,
memory_index: IndexVec<FieldIdx, u32>,
},
}
@ -1677,17 +1660,51 @@ impl<FieldIdx: Idx> FieldsShape<FieldIdx> {
}
}
#[inline]
pub fn memory_index(&self, i: usize) -> usize {
match *self {
FieldsShape::Primitive => {
unreachable!("FieldsShape::memory_index: `Primitive`s have no fields")
}
FieldsShape::Union(_) | FieldsShape::Array { .. } => i,
FieldsShape::Arbitrary { ref memory_index, .. } => {
memory_index[FieldIdx::new(i)].try_into().unwrap()
}
}
}
/// Gets source indices of the fields by increasing offsets.
#[inline]
pub fn index_by_increasing_offset(&self) -> impl ExactSizeIterator<Item = usize> {
let mut inverse_small = [0u8; 64];
let mut inverse_big = IndexVec::new();
let use_small = self.count() <= inverse_small.len();
// We have to write this logic twice in order to keep the array small.
if let FieldsShape::Arbitrary { ref memory_index, .. } = *self {
if use_small {
for (field_idx, &mem_idx) in memory_index.iter_enumerated() {
inverse_small[mem_idx as usize] = field_idx.index() as u8;
}
} else {
inverse_big = memory_index.invert_bijective_mapping();
}
}
// Primitives don't really have fields in the way that structs do,
// but having this return an empty iterator for them is unhelpful
// since that makes them look kinda like ZSTs, which they're not.
let pseudofield_count = if let FieldsShape::Primitive = self { 1 } else { self.count() };
(0..pseudofield_count).map(move |i| match self {
(0..pseudofield_count).map(move |i| match *self {
FieldsShape::Primitive | FieldsShape::Union(_) | FieldsShape::Array { .. } => i,
FieldsShape::Arbitrary { in_memory_order, .. } => in_memory_order[i as u32].index(),
FieldsShape::Arbitrary { .. } => {
if use_small {
inverse_small[i] as usize
} else {
inverse_big[i as u32].index()
}
}
})
}
}
@ -1719,10 +1736,6 @@ impl AddressSpace {
pub enum BackendRepr {
Scalar(Scalar),
ScalarPair(Scalar, Scalar),
ScalableVector {
element: Scalar,
count: u64,
},
SimdVector {
element: Scalar,
count: u64,
@ -1741,12 +1754,6 @@ impl BackendRepr {
match *self {
BackendRepr::Scalar(_)
| BackendRepr::ScalarPair(..)
// FIXME(rustc_scalable_vector): Scalable vectors are `Sized` while the
// `sized_hierarchy` feature is not yet fully implemented. After `sized_hierarchy` is
// fully implemented, scalable vectors will remain `Sized`, they just won't be
// `const Sized` - whether `is_unsized` continues to return `false` at that point will
// need to be revisited and will depend on what `is_unsized` is used for.
| BackendRepr::ScalableVector { .. }
| BackendRepr::SimdVector { .. } => false,
BackendRepr::Memory { sized } => !sized,
}
@ -1787,9 +1794,7 @@ impl BackendRepr {
BackendRepr::Scalar(s) => Some(s.align(cx).abi),
BackendRepr::ScalarPair(s1, s2) => Some(s1.align(cx).max(s2.align(cx)).abi),
// The align of a Vector can vary in surprising ways
BackendRepr::SimdVector { .. }
| BackendRepr::Memory { .. }
| BackendRepr::ScalableVector { .. } => None,
BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => None,
}
}
@ -1811,9 +1816,7 @@ impl BackendRepr {
Some(size)
}
// The size of a Vector can vary in surprising ways
BackendRepr::SimdVector { .. }
| BackendRepr::Memory { .. }
| BackendRepr::ScalableVector { .. } => None,
BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => None,
}
}
@ -1828,9 +1831,6 @@ impl BackendRepr {
BackendRepr::SimdVector { element: element.to_union(), count }
}
BackendRepr::Memory { .. } => BackendRepr::Memory { sized: true },
BackendRepr::ScalableVector { element, count } => {
BackendRepr::ScalableVector { element: element.to_union(), count }
}
}
}
@ -2071,9 +2071,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
/// Returns `true` if this is an aggregate type (including a ScalarPair!)
pub fn is_aggregate(&self) -> bool {
match self.backend_repr {
BackendRepr::Scalar(_)
| BackendRepr::SimdVector { .. }
| BackendRepr::ScalableVector { .. } => false,
BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } => false,
BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => true,
}
}
@ -2167,19 +2165,6 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
self.is_sized() && self.size.bytes() == 0 && self.align.bytes() == 1
}
/// Returns `true` if the size of the type is only known at runtime.
pub fn is_runtime_sized(&self) -> bool {
matches!(self.backend_repr, BackendRepr::ScalableVector { .. })
}
/// Returns the elements count of a scalable vector.
pub fn scalable_vector_element_count(&self) -> Option<u64> {
match self.backend_repr {
BackendRepr::ScalableVector { count, .. } => Some(count),
_ => None,
}
}
/// Returns `true` if the type is a ZST and not unsized.
///
/// Note that this does *not* imply that the type is irrelevant for layout! It can still have
@ -2188,7 +2173,6 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
match self.backend_repr {
BackendRepr::Scalar(_)
| BackendRepr::ScalarPair(..)
| BackendRepr::ScalableVector { .. }
| BackendRepr::SimdVector { .. } => false,
BackendRepr::Memory { sized } => sized && self.size.bytes() == 0,
}

View file

@ -10,9 +10,11 @@
// tidy-alphabetical-start
#![allow(clippy::mut_from_ref)] // Arena allocators are one place where this pattern is fine.
#![allow(internal_features)]
#![cfg_attr(bootstrap, feature(maybe_uninit_slice))]
#![cfg_attr(test, feature(test))]
#![deny(unsafe_op_in_unsafe_fn)]
#![doc(test(no_crate_inject, attr(deny(warnings), allow(internal_features))))]
#![feature(core_intrinsics)]
#![feature(decl_macro)]
#![feature(dropck_eyepatch)]
#![feature(never_type)]
@ -25,7 +27,7 @@ use std::cell::{Cell, RefCell};
use std::marker::PhantomData;
use std::mem::{self, MaybeUninit};
use std::ptr::{self, NonNull};
use std::{cmp, hint, slice};
use std::{cmp, intrinsics, slice};
use smallvec::SmallVec;
@ -171,22 +173,8 @@ impl<T> TypedArena<T> {
available_bytes >= additional_bytes
}
/// Allocates storage for `len >= 1` values in this arena, and returns a
/// raw pointer to the first value's storage.
///
/// # Safety
///
/// Caller must initialize each of the `len` slots to a droppable value
/// before the arena is dropped.
///
/// In practice, this typically means that the caller must be able to
/// raw-copy `len` already-initialized values into the slice without any
/// possibility of panicking.
///
/// FIXME(Zalathar): This is *very* fragile; perhaps we need a different
/// approach to arena-allocating slices of droppable values.
#[inline]
unsafe fn alloc_raw_slice(&self, len: usize) -> *mut T {
fn alloc_raw_slice(&self, len: usize) -> *mut T {
assert!(size_of::<T>() != 0);
assert!(len != 0);
@ -221,7 +209,7 @@ impl<T> TypedArena<T> {
&self,
iter: impl IntoIterator<Item = Result<T, E>>,
) -> Result<&mut [T], E> {
// Despite the similarity with `DroplessArena`, we cannot reuse their fast case. The reason
// Despite the similarlty with `DroplessArena`, we cannot reuse their fast case. The reason
// is subtle: these arenas are reentrant. In other words, `iter` may very well be holding a
// reference to `self` and adding elements to the arena during iteration.
//
@ -242,15 +230,9 @@ impl<T> TypedArena<T> {
}
// Move the content to the arena by copying and then forgetting it.
let len = vec.len();
// SAFETY: After allocating raw storage for exactly `len` values, we
// must fully initialize the storage without panicking, and we must
// also prevent the stale values in the vec from being dropped.
Ok(unsafe {
let start_ptr = self.alloc_raw_slice(len);
// Initialize the newly-allocated storage without panicking.
Ok(unsafe {
vec.as_ptr().copy_to_nonoverlapping(start_ptr, len);
// Prevent the stale values in the vec from being dropped.
vec.set_len(0);
slice::from_raw_parts_mut(start_ptr, len)
})
@ -451,7 +433,7 @@ impl DroplessArena {
let bytes = align_up(layout.size(), DROPLESS_ALIGNMENT);
// Tell LLVM that `end` is aligned to DROPLESS_ALIGNMENT.
unsafe { hint::assert_unchecked(end == align_down(end, DROPLESS_ALIGNMENT)) };
unsafe { intrinsics::assume(end == align_down(end, DROPLESS_ALIGNMENT)) };
if let Some(sub) = end.checked_sub(bytes) {
let new_end = align_down(sub, layout.align());
@ -509,6 +491,19 @@ impl DroplessArena {
}
}
/// Used by `Lift` to check whether this slice is allocated
/// in this arena.
#[inline]
pub fn contains_slice<T>(&self, slice: &[T]) -> bool {
for chunk in self.chunks.borrow_mut().iter_mut() {
let ptr = slice.as_ptr().cast::<u8>().cast_mut();
if chunk.start() <= ptr && chunk.end() >= ptr {
return true;
}
}
false
}
/// Allocates a string slice that is copied into the `DroplessArena`, returning a
/// reference to it. Will panic if passed an empty string.
///
@ -590,7 +585,7 @@ impl DroplessArena {
&self,
iter: impl IntoIterator<Item = Result<T, E>>,
) -> Result<&mut [T], E> {
// Despite the similarity with `alloc_from_iter`, we cannot reuse their fast case, as we
// Despite the similarlty with `alloc_from_iter`, we cannot reuse their fast case, as we
// cannot know the minimum length of the iterator in this case.
assert!(size_of::<T>() != 0);
@ -624,7 +619,7 @@ impl DroplessArena {
/// - Types that are `!Copy` and `Drop`: these must be specified in the
/// arguments. The `TypedArena` will be used for them.
///
#[rustc_macro_transparency = "semiopaque"]
#[rustc_macro_transparency = "semitransparent"]
pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
#[derive(Default)]
pub struct Arena<'tcx> {

View file

@ -7,7 +7,7 @@ edition = "2024"
# tidy-alphabetical-start
bitflags = "2.4.1"
memchr = "2.7.6"
rustc-literal-escaper = "0.0.7"
rustc-literal-escaper = "0.0.5"
rustc_ast_ir = { path = "../rustc_ast_ir" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_index = { path = "../rustc_index" }

View file

@ -34,7 +34,6 @@ use rustc_span::source_map::{Spanned, respan};
use rustc_span::{ByteSymbol, DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
use thin_vec::{ThinVec, thin_vec};
use crate::attr::data_structures::CfgEntry;
pub use crate::format::*;
use crate::token::{self, CommentKind, Delimiter};
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
@ -142,11 +141,16 @@ impl Path {
/// Check if this path is potentially a trivial const arg, i.e., one that can _potentially_
/// be represented without an anon const in the HIR.
///
/// Returns true iff the path has exactly one segment, and it has no generic args
/// If `allow_mgca_arg` is true (as should be the case in most situations when
/// `#![feature(min_generic_const_args)]` is enabled), then this always returns true
/// because all paths are valid.
///
/// Otherwise, it returns true iff the path has exactly one segment, and it has no generic args
/// (i.e., it is _potentially_ a const parameter).
#[tracing::instrument(level = "debug", ret)]
pub fn is_potential_trivial_const_arg(&self) -> bool {
self.segments.len() == 1 && self.segments.iter().all(|seg| seg.args.is_none())
pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
allow_mgca_arg
|| self.segments.len() == 1 && self.segments.iter().all(|seg| seg.args.is_none())
}
}
@ -656,7 +660,11 @@ impl Pat {
// A tuple pattern `(P0, .., Pn)` can be reparsed as `(T0, .., Tn)`
// assuming `T0` to `Tn` are all syntactically valid as types.
PatKind::Tuple(pats) => {
let tys = pats.iter().map(|pat| pat.to_ty()).collect::<Option<ThinVec<_>>>()?;
let mut tys = ThinVec::with_capacity(pats.len());
// FIXME(#48994) - could just be collected into an Option<Vec>
for pat in pats {
tys.push(pat.to_ty()?);
}
TyKind::Tup(tys)
}
_ => return None,
@ -1251,19 +1259,6 @@ pub enum StmtKind {
MacCall(Box<MacCallStmt>),
}
impl StmtKind {
pub fn descr(&self) -> &'static str {
match self {
StmtKind::Let(_) => "local",
StmtKind::Item(_) => "item",
StmtKind::Expr(_) => "expression",
StmtKind::Semi(_) => "statement",
StmtKind::Empty => "semicolon",
StmtKind::MacCall(_) => "macro call",
}
}
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct MacCallStmt {
pub mac: Box<MacCall>,
@ -1377,15 +1372,6 @@ pub enum UnsafeSource {
UserProvided,
}
/// Track whether under `feature(min_generic_const_args)` this anon const
/// was explicitly disambiguated as an anon const or not through the use of
/// `const { ... }` syntax.
#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, Walkable)]
pub enum MgcaDisambiguation {
AnonConst,
Direct,
}
/// A constant (expression) that's not an item or associated item,
/// but needs its own `DefId` for type-checking, const-eval, etc.
/// These are usually found nested inside types (e.g., array lengths)
@ -1395,7 +1381,6 @@ pub enum MgcaDisambiguation {
pub struct AnonConst {
pub id: NodeId,
pub value: Box<Expr>,
pub mgca_disambiguation: MgcaDisambiguation,
}
/// An expression.
@ -1414,22 +1399,28 @@ impl Expr {
///
/// This will unwrap at most one block level (curly braces). After that, if the expression
/// is a path, it mostly dispatches to [`Path::is_potential_trivial_const_arg`].
/// See there for more info about `allow_mgca_arg`.
///
/// This function will only allow paths with no qself, before dispatching to the `Path`
/// function of the same name.
/// The only additional thing to note is that when `allow_mgca_arg` is false, this function
/// will only allow paths with no qself, before dispatching to the `Path` function of
/// the same name.
///
/// Does not ensure that the path resolves to a const param/item, the caller should check this.
/// This also does not consider macros, so it's only correct after macro-expansion.
pub fn is_potential_trivial_const_arg(&self) -> bool {
pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
let this = self.maybe_unwrap_block();
if allow_mgca_arg {
matches!(this.kind, ExprKind::Path(..))
} else {
if let ExprKind::Path(None, path) = &this.kind
&& path.is_potential_trivial_const_arg()
&& path.is_potential_trivial_const_arg(allow_mgca_arg)
{
true
} else {
false
}
}
}
/// Returns an expression with (when possible) *one* outer brace removed
pub fn maybe_unwrap_block(&self) -> &Expr {
@ -1530,10 +1521,11 @@ impl Expr {
// then type of result is trait object.
// Otherwise we don't assume the result type.
ExprKind::Binary(binop, lhs, rhs) if binop.node == BinOpKind::Add => {
let (Some(lhs), Some(rhs)) = (lhs.to_bound(), rhs.to_bound()) else {
return None;
};
if let (Some(lhs), Some(rhs)) = (lhs.to_bound(), rhs.to_bound()) {
TyKind::TraitObject(vec![lhs, rhs], TraitObjectSyntax::None)
} else {
return None;
}
}
ExprKind::Underscore => TyKind::Infer,
@ -1807,21 +1799,15 @@ pub enum ExprKind {
/// or a `gen` block (`gen move { ... }`).
///
/// The span is the "decl", which is the header before the body `{ }`
/// including the `async`/`gen` keywords and possibly `move`.
/// including the `asyng`/`gen` keywords and possibly `move`.
Gen(CaptureBy, Box<Block>, GenBlockKind, Span),
/// An await expression (`my_future.await`). Span is of await keyword.
Await(Box<Expr>, Span),
/// A use expression (`x.use`). Span is of use keyword.
Use(Box<Expr>, Span),
/// A try block (`try { ... }`), if the type is `None`, or
/// A try block (`try bikeshed Ty { ... }`) if the type is `Some`.
///
/// Note that `try bikeshed` is a *deliberately ridiculous* placeholder
/// syntax to avoid deciding what keyword or symbol should go there.
/// It's that way for experimentation only; an RFC to decide the final
/// semantics and syntax would be needed to put it on stabilization-track.
TryBlock(Box<Block>, Option<Box<Ty>>),
/// A try block (`try { ... }`).
TryBlock(Box<Block>),
/// An assignment (`a = foo()`).
/// The `Span` argument is the span of the `=` token.
@ -2104,20 +2090,6 @@ pub struct MacroDef {
pub body: Box<DelimArgs>,
/// `true` if macro was defined with `macro_rules`.
pub macro_rules: bool,
/// Corresponds to `#[eii_declaration(...)]`.
/// `#[eii_declaration(...)]` is a built-in attribute macro, not a built-in attribute,
/// because we require some name resolution to occur in the parameters of this attribute.
/// Name resolution isn't possible in attributes otherwise, so we encode it in the AST.
/// During ast lowering, we turn it back into an attribute again
pub eii_declaration: Option<EiiDecl>,
}
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
pub struct EiiDecl {
/// path to the extern item we're targeting
pub foreign_item: Path,
pub impl_unsafe: bool,
}
#[derive(Clone, Encodable, Decodable, Debug, Copy, Hash, Eq, PartialEq)]
@ -2392,7 +2364,7 @@ impl FnSig {
/// * the `G<Ty> = Ty` in `Trait<G<Ty> = Ty>`
/// * the `A: Bound` in `Trait<A: Bound>`
/// * the `RetTy` in `Trait(ArgTy, ArgTy) -> RetTy`
/// * the `C = { Ct }` in `Trait<C = { Ct }>` (feature `min_generic_const_args`)
/// * the `C = { Ct }` in `Trait<C = { Ct }>` (feature `associated_const_equality`)
/// * the `f(..): Bound` in `Trait<f(..): Bound>` (feature `return_type_notation`)
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct AssocItemConstraint {
@ -3131,16 +3103,8 @@ pub enum Const {
/// For details see the [RFC #2532](https://github.com/rust-lang/rfcs/pull/2532).
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
pub enum Defaultness {
/// Item is unmarked. Implicitly determined based off of position.
/// For impls, this is `final`; for traits, this is `default`.
///
/// If you're expanding an item in a built-in macro or parsing an item
/// by hand, you probably want to use this.
Implicit,
/// `default`
Default(Span),
/// `final`; per RFC 3678, only trait items may be *explicitly* marked final.
Final(Span),
Final,
}
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)]
@ -3352,8 +3316,7 @@ impl UseTree {
/// Distinguishes between `Attribute`s that decorate items and Attributes that
/// are contained as statements within items. These two cases need to be
/// distinguished for pretty-printing.
#[derive(Clone, PartialEq, Eq, Hash, Debug, Copy)]
#[derive(Encodable, Decodable, HashStable_Generic, Walkable)]
#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, HashStable_Generic, Walkable)]
pub enum AttrStyle {
Outer,
Inner,
@ -3397,7 +3360,7 @@ impl NormalAttr {
item: AttrItem {
unsafety: Safety::Default,
path: Path::from_ident(ident),
args: AttrItemKind::Unparsed(AttrArgs::Empty),
args: AttrArgs::Empty,
tokens: None,
},
tokens: None,
@ -3409,53 +3372,11 @@ impl NormalAttr {
pub struct AttrItem {
pub unsafety: Safety,
pub path: Path,
pub args: AttrItemKind,
pub args: AttrArgs,
// Tokens for the meta item, e.g. just the `foo` within `#[foo]` or `#![foo]`.
pub tokens: Option<LazyAttrTokenStream>,
}
/// Some attributes are stored in a parsed form, for performance reasons.
/// Their arguments don't have to be reparsed everytime they're used
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub enum AttrItemKind {
Parsed(EarlyParsedAttribute),
Unparsed(AttrArgs),
}
impl AttrItemKind {
pub fn unparsed(self) -> Option<AttrArgs> {
match self {
AttrItemKind::Unparsed(args) => Some(args),
AttrItemKind::Parsed(_) => None,
}
}
pub fn unparsed_ref(&self) -> Option<&AttrArgs> {
match self {
AttrItemKind::Unparsed(args) => Some(args),
AttrItemKind::Parsed(_) => None,
}
}
pub fn span(&self) -> Option<Span> {
match self {
AttrItemKind::Unparsed(args) => args.span(),
AttrItemKind::Parsed(_) => None,
}
}
}
/// Some attributes are stored in parsed form in the AST.
/// This is done for performance reasons, so the attributes don't need to be reparsed on every use.
///
/// Currently all early parsed attributes are excluded from pretty printing at rustc_ast_pretty::pprust::state::print_attribute_inline.
/// When adding new early parsed attributes, consider whether they should be pretty printed.
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
pub enum EarlyParsedAttribute {
CfgTrace(CfgEntry),
CfgAttrTrace,
}
impl AttrItem {
pub fn is_valid_for_outer_style(&self) -> bool {
self.path == sym::cfg_attr
@ -3630,7 +3551,6 @@ impl Item {
pub fn opt_generics(&self) -> Option<&Generics> {
match &self.kind {
ItemKind::ExternCrate(..)
| ItemKind::ConstBlock(_)
| ItemKind::Use(_)
| ItemKind::Mod(..)
| ItemKind::ForeignMod(_)
@ -3809,34 +3729,6 @@ pub struct Fn {
pub contract: Option<Box<FnContract>>,
pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
pub body: Option<Box<Block>>,
/// This function is an implementation of an externally implementable item (EII).
/// This means, there was an EII declared somewhere and this function is the
/// implementation that should be run when the declaration is called.
pub eii_impls: ThinVec<EiiImpl>,
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct EiiImpl {
pub node_id: NodeId,
pub eii_macro_path: Path,
/// This field is an implementation detail that prevents a lot of bugs.
/// See <https://github.com/rust-lang/rust/issues/149981> for an example.
///
/// The problem is, that if we generate a declaration *together* with its default,
/// we generate both a declaration and an implementation. The generated implementation
/// uses the same mechanism to register itself as a user-defined implementation would,
/// despite being invisible to users. What does happen is a name resolution step.
/// The invisible default implementation has to find the declaration.
/// Both are generated at the same time, so we can skip that name resolution step.
///
/// This field is that shortcut: we prefill the extern target to skip a name resolution step,
/// making sure it never fails. It'd be awful UX if we fail name resolution in code invisible to the user.
pub known_eii_macro_resolution: Option<EiiDecl>,
pub impl_safety: Safety,
pub span: Span,
pub inner_span: Span,
pub is_default: bool,
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
@ -3877,55 +3769,27 @@ pub struct ConstItem {
pub ident: Ident,
pub generics: Generics,
pub ty: Box<Ty>,
pub rhs_kind: ConstItemRhsKind,
pub rhs: Option<ConstItemRhs>,
pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub enum ConstItemRhsKind {
Body { rhs: Option<Box<Expr>> },
TypeConst { rhs: Option<AnonConst> },
pub enum ConstItemRhs {
TypeConst(AnonConst),
Body(Box<Expr>),
}
impl ConstItemRhsKind {
pub fn new_body(rhs: Box<Expr>) -> Self {
Self::Body { rhs: Some(rhs) }
impl ConstItemRhs {
pub fn span(&self) -> Span {
self.expr().span
}
pub fn span(&self) -> Option<Span> {
Some(self.expr()?.span)
}
pub fn expr(&self) -> Option<&Expr> {
pub fn expr(&self) -> &Expr {
match self {
Self::Body { rhs: Some(body) } => Some(&body),
Self::TypeConst { rhs: Some(anon) } => Some(&anon.value),
_ => None,
ConstItemRhs::TypeConst(anon_const) => &anon_const.value,
ConstItemRhs::Body(expr) => expr,
}
}
pub fn has_expr(&self) -> bool {
match self {
Self::Body { rhs: Some(_) } => true,
Self::TypeConst { rhs: Some(_) } => true,
_ => false,
}
}
pub fn is_type_const(&self) -> bool {
matches!(self, &Self::TypeConst { .. })
}
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct ConstBlockItem {
pub id: NodeId,
pub span: Span,
pub block: Box<Block>,
}
impl ConstBlockItem {
pub const IDENT: Ident = Ident { name: kw::Underscore, span: DUMMY_SP };
}
// Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`.
@ -3947,11 +3811,6 @@ pub enum ItemKind {
///
/// E.g., `const FOO: i32 = 42;`.
Const(Box<ConstItem>),
/// A module-level const block.
/// Equivalent to `const _: () = const { ... };`.
///
/// E.g., `const { assert!(true) }`.
ConstBlock(ConstBlockItem),
/// A function declaration (`fn`).
///
/// E.g., `fn foo(bar: usize) -> usize { .. }`.
@ -4028,8 +3887,6 @@ impl ItemKind {
| ItemKind::MacroDef(ident, _)
| ItemKind::Delegation(box Delegation { ident, .. }) => Some(ident),
ItemKind::ConstBlock(_) => Some(ConstBlockItem::IDENT),
ItemKind::Use(_)
| ItemKind::ForeignMod(_)
| ItemKind::GlobalAsm(_)
@ -4043,9 +3900,9 @@ impl ItemKind {
pub fn article(&self) -> &'static str {
use ItemKind::*;
match self {
Use(..) | Static(..) | Const(..) | ConstBlock(..) | Fn(..) | Mod(..)
| GlobalAsm(..) | TyAlias(..) | Struct(..) | Union(..) | Trait(..) | TraitAlias(..)
| MacroDef(..) | Delegation(..) | DelegationMac(..) => "a",
Use(..) | Static(..) | Const(..) | Fn(..) | Mod(..) | GlobalAsm(..) | TyAlias(..)
| Struct(..) | Union(..) | Trait(..) | TraitAlias(..) | MacroDef(..)
| Delegation(..) | DelegationMac(..) => "a",
ExternCrate(..) | ForeignMod(..) | MacCall(..) | Enum(..) | Impl { .. } => "an",
}
}
@ -4056,7 +3913,6 @@ impl ItemKind {
ItemKind::Use(..) => "`use` import",
ItemKind::Static(..) => "static item",
ItemKind::Const(..) => "constant item",
ItemKind::ConstBlock(..) => "const block",
ItemKind::Fn(..) => "function",
ItemKind::Mod(..) => "module",
ItemKind::ForeignMod(..) => "extern block",
@ -4086,18 +3942,7 @@ impl ItemKind {
| Self::Trait(box Trait { generics, .. })
| Self::TraitAlias(box TraitAlias { generics, .. })
| Self::Impl(Impl { generics, .. }) => Some(generics),
Self::ExternCrate(..)
| Self::Use(..)
| Self::Static(..)
| Self::ConstBlock(..)
| Self::Mod(..)
| Self::ForeignMod(..)
| Self::GlobalAsm(..)
| Self::MacCall(..)
| Self::MacroDef(..)
| Self::Delegation(..)
| Self::DelegationMac(..) => None,
_ => None,
}
}
}
@ -4148,7 +3993,7 @@ impl AssocItemKind {
| Self::Fn(box Fn { defaultness, .. })
| Self::Type(box TyAlias { defaultness, .. }) => defaultness,
Self::MacCall(..) | Self::Delegation(..) | Self::DelegationMac(..) => {
Defaultness::Implicit
Defaultness::Final
}
}
}
@ -4250,7 +4095,7 @@ mod size_asserts {
static_assert_size!(Block, 32);
static_assert_size!(Expr, 72);
static_assert_size!(ExprKind, 40);
static_assert_size!(Fn, 192);
static_assert_size!(Fn, 184);
static_assert_size!(ForeignItem, 80);
static_assert_size!(ForeignItemKind, 16);
static_assert_size!(GenericArg, 24);

View file

@ -1,101 +0,0 @@
use std::fmt;
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
use rustc_span::{Span, Symbol};
use thin_vec::ThinVec;
use crate::attr::version::RustcVersion;
#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash, HashStable_Generic)]
pub enum CfgEntry {
All(ThinVec<CfgEntry>, Span),
Any(ThinVec<CfgEntry>, Span),
Not(Box<CfgEntry>, Span),
Bool(bool, Span),
NameValue { name: Symbol, value: Option<Symbol>, span: Span },
Version(Option<RustcVersion>, Span),
}
impl CfgEntry {
pub fn lower_spans(&mut self, lower_span: impl Copy + Fn(Span) -> Span) {
match self {
CfgEntry::All(subs, span) | CfgEntry::Any(subs, span) => {
*span = lower_span(*span);
subs.iter_mut().for_each(|sub| sub.lower_spans(lower_span));
}
CfgEntry::Not(sub, span) => {
*span = lower_span(*span);
sub.lower_spans(lower_span);
}
CfgEntry::Bool(_, span)
| CfgEntry::NameValue { span, .. }
| CfgEntry::Version(_, span) => {
*span = lower_span(*span);
}
}
}
pub fn span(&self) -> Span {
let (Self::All(_, span)
| Self::Any(_, span)
| Self::Not(_, span)
| Self::Bool(_, span)
| Self::NameValue { span, .. }
| Self::Version(_, span)) = self;
*span
}
/// Same as `PartialEq` but doesn't check spans and ignore order of cfgs.
pub fn is_equivalent_to(&self, other: &Self) -> bool {
match (self, other) {
(Self::All(a, _), Self::All(b, _)) | (Self::Any(a, _), Self::Any(b, _)) => {
a.len() == b.len() && a.iter().all(|a| b.iter().any(|b| a.is_equivalent_to(b)))
}
(Self::Not(a, _), Self::Not(b, _)) => a.is_equivalent_to(b),
(Self::Bool(a, _), Self::Bool(b, _)) => a == b,
(
Self::NameValue { name: name1, value: value1, .. },
Self::NameValue { name: name2, value: value2, .. },
) => name1 == name2 && value1 == value2,
(Self::Version(a, _), Self::Version(b, _)) => a == b,
_ => false,
}
}
}
impl fmt::Display for CfgEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn write_entries(
name: &str,
entries: &[CfgEntry],
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
write!(f, "{name}(")?;
for (nb, entry) in entries.iter().enumerate() {
if nb != 0 {
f.write_str(", ")?;
}
entry.fmt(f)?;
}
f.write_str(")")
}
match self {
Self::All(entries, _) => write_entries("all", entries, f),
Self::Any(entries, _) => write_entries("any", entries, f),
Self::Not(entry, _) => write!(f, "not({entry})"),
Self::Bool(value, _) => write!(f, "{value}"),
Self::NameValue { name, value, .. } => {
match value {
// We use `as_str` and debug display to have characters escaped and `"`
// characters surrounding the string.
Some(value) => write!(f, "{name} = {:?}", value.as_str()),
None => write!(f, "{name}"),
}
}
Self::Version(version, _) => match version {
Some(version) => write!(f, "{version}"),
None => Ok(()),
},
}
}
}

View file

@ -1,8 +1,5 @@
//! Functions dealing with attributes and meta items.
pub mod data_structures;
pub mod version;
use std::fmt::Debug;
use std::sync::atomic::{AtomicU32, Ordering};
@ -11,15 +8,12 @@ use rustc_span::{Ident, Span, Symbol, sym};
use smallvec::{SmallVec, smallvec};
use thin_vec::{ThinVec, thin_vec};
use crate::AttrItemKind;
use crate::ast::{
AttrArgs, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID, DelimArgs,
Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path,
PathSegment, Safety,
};
use crate::token::{
self, CommentKind, Delimiter, DocFragmentKind, InvisibleOrigin, MetaVarKind, Token,
};
use crate::token::{self, CommentKind, Delimiter, InvisibleOrigin, MetaVarKind, Token};
use crate::tokenstream::{
DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenStreamIter, TokenTree,
};
@ -66,15 +60,6 @@ impl Attribute {
}
}
/// Replaces the arguments of this attribute with new arguments `AttrItemKind`.
/// This is useful for making this attribute into a trace attribute, and should otherwise be avoided.
pub fn replace_args(&mut self, new_args: AttrItemKind) {
match &mut self.kind {
AttrKind::Normal(normal) => normal.item.args = new_args,
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
}
}
pub fn unwrap_normal_item(self) -> AttrItem {
match self.kind {
AttrKind::Normal(normal) => normal.item,
@ -90,7 +75,7 @@ impl AttributeExt for Attribute {
fn value_span(&self) -> Option<Span> {
match &self.kind {
AttrKind::Normal(normal) => match &normal.item.args.unparsed_ref()? {
AttrKind::Normal(normal) => match &normal.item.args {
AttrArgs::Eq { expr, .. } => Some(expr.span),
_ => None,
},
@ -109,11 +94,11 @@ impl AttributeExt for Attribute {
}
/// For a single-segment attribute, returns its name; otherwise, returns `None`.
fn name(&self) -> Option<Symbol> {
fn ident(&self) -> Option<Ident> {
match &self.kind {
AttrKind::Normal(normal) => {
if let [ident] = &*normal.item.path.segments {
Some(ident.ident.name)
Some(ident.ident)
} else {
None
}
@ -122,18 +107,9 @@ impl AttributeExt for Attribute {
}
}
fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>> {
fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
match &self.kind {
AttrKind::Normal(p) => {
Some(p.item.path.segments.iter().map(|i| i.ident.name).collect())
}
AttrKind::DocComment(_, _) => None,
}
}
fn path_span(&self) -> Option<Span> {
match &self.kind {
AttrKind::Normal(attr) => Some(attr.item.path.span),
AttrKind::Normal(p) => Some(p.item.path.segments.iter().map(|i| i.ident).collect()),
AttrKind::DocComment(_, _) => None,
}
}
@ -160,7 +136,7 @@ impl AttributeExt for Attribute {
fn is_word(&self) -> bool {
if let AttrKind::Normal(normal) = &self.kind {
matches!(normal.item.args, AttrItemKind::Unparsed(AttrArgs::Empty))
matches!(normal.item.args, AttrArgs::Empty)
} else {
false
}
@ -203,21 +179,15 @@ impl AttributeExt for Attribute {
}
/// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
/// * `///doc` returns `Some(("doc", DocFragmentKind::Sugared(CommentKind::Line)))`.
/// * `/** doc */` returns `Some(("doc", DocFragmentKind::Sugared(CommentKind::Block)))`.
/// * `#[doc = "doc"]` returns `Some(("doc", DocFragmentKind::Raw))`.
/// * `///doc` returns `Some(("doc", CommentKind::Line))`.
/// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
/// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
/// * `#[doc(...)]` returns `None`.
fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
match &self.kind {
AttrKind::DocComment(kind, data) => Some((*data, DocFragmentKind::Sugared(*kind))),
AttrKind::DocComment(kind, data) => Some((*data, *kind)),
AttrKind::Normal(normal) if normal.item.path == sym::doc => {
if let Some(value) = normal.item.value_str()
&& let Some(value_span) = normal.item.value_span()
{
Some((value, DocFragmentKind::Raw(value_span)))
} else {
None
}
normal.item.value_str().map(|s| (s, CommentKind::Line))
}
_ => None,
}
@ -235,34 +205,6 @@ impl AttributeExt for Attribute {
}
}
fn deprecation_note(&self) -> Option<Ident> {
match &self.kind {
AttrKind::Normal(normal) if normal.item.path == sym::deprecated => {
let meta = &normal.item;
// #[deprecated = "..."]
if let Some(s) = meta.value_str() {
return Some(Ident { name: s, span: meta.span() });
}
// #[deprecated(note = "...")]
if let Some(list) = meta.meta_item_list() {
for nested in list {
if let Some(mi) = nested.meta_item()
&& mi.path == sym::note
&& let Some(s) = mi.value_str()
{
return Some(Ident { name: s, span: mi.span });
}
}
}
None
}
_ => None,
}
}
fn doc_resolution_scope(&self) -> Option<AttrStyle> {
match &self.kind {
AttrKind::DocComment(..) => Some(self.style),
@ -278,24 +220,6 @@ impl AttributeExt for Attribute {
fn is_automatically_derived_attr(&self) -> bool {
self.has_name(sym::automatically_derived)
}
fn is_doc_hidden(&self) -> bool {
self.has_name(sym::doc)
&& self.meta_item_list().is_some_and(|l| list_contains_name(&l, sym::hidden))
}
fn is_doc_keyword_or_attribute(&self) -> bool {
if self.has_name(sym::doc)
&& let Some(items) = self.meta_item_list()
{
for item in items {
if item.has_name(sym::keyword) || item.has_name(sym::attribute) {
return true;
}
}
}
false
}
}
impl Attribute {
@ -305,7 +229,6 @@ impl Attribute {
pub fn may_have_doc_links(&self) -> bool {
self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
|| self.deprecation_note().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
}
/// Extracts the MetaItem from inside this Attribute.
@ -345,7 +268,7 @@ impl AttrItem {
}
pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
match &self.args.unparsed_ref()? {
match &self.args {
AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
MetaItemKind::list_from_tokens(args.tokens.clone())
}
@ -366,7 +289,7 @@ impl AttrItem {
/// #[attr("value")]
/// ```
fn value_str(&self) -> Option<Symbol> {
match &self.args.unparsed_ref()? {
match &self.args {
AttrArgs::Eq { expr, .. } => match expr.kind {
ExprKind::Lit(token_lit) => {
LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())
@ -377,25 +300,6 @@ impl AttrItem {
}
}
/// Returns the span in:
///
/// ```text
/// #[attribute = "value"]
/// ^^^^^^^
/// ```
///
/// It returns `None` in any other cases like:
///
/// ```text
/// #[attr("value")]
/// ```
fn value_span(&self) -> Option<Span> {
match &self.args.unparsed_ref()? {
AttrArgs::Eq { expr, .. } => Some(expr.span),
AttrArgs::Delimited(_) | AttrArgs::Empty => None,
}
}
pub fn meta(&self, span: Span) -> Option<MetaItem> {
Some(MetaItem {
unsafety: Safety::Default,
@ -406,7 +310,7 @@ impl AttrItem {
}
pub fn meta_kind(&self) -> Option<MetaItemKind> {
MetaItemKind::from_attr_args(self.args.unparsed_ref()?)
MetaItemKind::from_attr_args(&self.args)
}
}
@ -498,17 +402,20 @@ impl MetaItem {
thin_vec![PathSegment::path_root(span)]
};
loop {
let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
iter.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref()
else {
return None;
};
{
segments.push(PathSegment::from_ident(Ident::new(name, span)));
let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) = iter.peek()
else {
break;
};
} else {
return None;
}
if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
iter.peek()
{
iter.next();
} else {
break;
}
}
let span = span.with_hi(segments.last().unwrap().ident.span.hi());
Path { span, segments, tokens: None }
@ -741,13 +648,7 @@ fn mk_attr(
args: AttrArgs,
span: Span,
) -> Attribute {
mk_attr_from_item(
g,
AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args), tokens: None },
None,
style,
span,
)
mk_attr_from_item(g, AttrItem { unsafety, path, args, tokens: None }, None, style, span)
}
pub fn mk_attr_from_item(
@ -851,7 +752,9 @@ pub trait AttributeExt: Debug {
/// For a single-segment attribute (i.e., `#[attr]` and not `#[path::atrr]`),
/// return the name of the attribute; otherwise, returns `None`.
fn name(&self) -> Option<Symbol>;
fn name(&self) -> Option<Symbol> {
self.ident().map(|ident| ident.name)
}
/// Get the meta item list, `#[attr(meta item list)]`
fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>>;
@ -862,6 +765,9 @@ pub trait AttributeExt: Debug {
/// Gets the span of the value literal, as string, when using `#[attr = value]`
fn value_span(&self) -> Option<Span>;
/// For a single-segment attribute, returns its ident; otherwise, returns `None`.
fn ident(&self) -> Option<Ident>;
/// Checks whether the path of this attribute matches the name.
///
/// Matches one segment of the path to each element in `name`
@ -874,7 +780,7 @@ pub trait AttributeExt: Debug {
#[inline]
fn has_name(&self, name: Symbol) -> bool {
self.name().map(|x| x == name).unwrap_or(false)
self.ident().map(|x| x.name == name).unwrap_or(false)
}
#[inline]
@ -888,13 +794,13 @@ pub trait AttributeExt: Debug {
fn is_word(&self) -> bool;
fn path(&self) -> SmallVec<[Symbol; 1]> {
self.symbol_path().unwrap_or(smallvec![sym::doc])
self.ident_path()
.map(|i| i.into_iter().map(|i| i.name).collect())
.unwrap_or(smallvec![sym::doc])
}
fn path_span(&self) -> Option<Span>;
/// Returns None for doc comments
fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>>;
fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>>;
/// Returns the documentation if this is a doc comment or a sugared doc comment.
/// * `///doc` returns `Some("doc")`.
@ -902,11 +808,6 @@ pub trait AttributeExt: Debug {
/// * `#[doc(...)]` returns `None`.
fn doc_str(&self) -> Option<Symbol>;
/// Returns the deprecation note if this is deprecation attribute.
/// * `#[deprecated = "note"]` returns `Some("note")`.
/// * `#[deprecated(note = "note", ...)]` returns `Some("note")`.
fn deprecation_note(&self) -> Option<Ident>;
fn is_proc_macro_attr(&self) -> bool {
[sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
.iter()
@ -919,7 +820,7 @@ pub trait AttributeExt: Debug {
/// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
/// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
/// * `#[doc(...)]` returns `None`.
fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)>;
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)>;
/// Returns outer or inner if this is a doc attribute or a sugared doc
/// comment, otherwise None.
@ -929,12 +830,6 @@ pub trait AttributeExt: Debug {
/// commented module (for inner doc) vs within its parent module (for outer
/// doc).
fn doc_resolution_scope(&self) -> Option<AttrStyle>;
/// Returns `true` if this attribute contains `doc(hidden)`.
fn is_doc_hidden(&self) -> bool;
/// Returns `true` is this attribute contains `doc(keyword)` or `doc(attribute)`.
fn is_doc_keyword_or_attribute(&self) -> bool;
}
// FIXME(fn_delegation): use function delegation instead of manually forwarding
@ -960,6 +855,10 @@ impl Attribute {
AttributeExt::value_span(self)
}
pub fn ident(&self) -> Option<Ident> {
AttributeExt::ident(self)
}
pub fn path_matches(&self, name: &[Symbol]) -> bool {
AttributeExt::path_matches(self, name)
}
@ -991,6 +890,10 @@ impl Attribute {
AttributeExt::path(self)
}
pub fn ident_path(&self) -> Option<SmallVec<[Ident; 1]>> {
AttributeExt::ident_path(self)
}
pub fn doc_str(&self) -> Option<Symbol> {
AttributeExt::doc_str(self)
}
@ -999,7 +902,7 @@ impl Attribute {
AttributeExt::is_proc_macro_attr(self)
}
pub fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
AttributeExt::doc_str_and_fragment_kind(self)
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
AttributeExt::doc_str_and_comment_kind(self)
}
}

View file

@ -1,43 +0,0 @@
use std::fmt::{self, Display};
use std::sync::OnceLock;
use rustc_macros::{BlobDecodable, Encodable, HashStable_Generic, current_rustc_version};
#[derive(Encodable, BlobDecodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(HashStable_Generic)]
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

@ -6,6 +6,7 @@
// tidy-alphabetical-start
#![doc(test(attr(deny(warnings), allow(internal_features))))]
#![feature(array_windows)]
#![feature(associated_type_defaults)]
#![feature(box_patterns)]
#![feature(if_let_guard)]

View file

@ -16,37 +16,13 @@ use rustc_span::{Ident, Symbol};
use crate::ast;
use crate::util::case::Case;
/// Represents the kind of doc comment it is, ie `///` or `#[doc = ""]`.
#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)]
pub enum DocFragmentKind {
/// A sugared doc comment: `///` or `//!` or `/**` or `/*!`.
Sugared(CommentKind),
/// A "raw" doc comment: `#[doc = ""]`. The `Span` represents the string literal.
Raw(Span),
}
impl DocFragmentKind {
pub fn is_sugared(self) -> bool {
matches!(self, Self::Sugared(_))
}
/// If it is `Sugared`, it will return its associated `CommentKind`, otherwise it will return
/// `CommentKind::Line`.
pub fn comment_kind(self) -> CommentKind {
match self {
Self::Sugared(kind) => kind,
Self::Raw(_) => CommentKind::Line,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, Debug, HashStable_Generic)]
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
pub enum CommentKind {
Line,
Block,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, 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),
@ -123,7 +99,7 @@ impl fmt::Display for MetaVarKind {
/// Describes how a sequence of token trees is delimited.
/// Cannot use `proc_macro::Delimiter` directly because this
/// structure should implement some additional traits.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable, HashStable_Generic)]
#[derive(Copy, Clone, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
pub enum Delimiter {
/// `( ... )`
Parenthesis,
@ -186,7 +162,7 @@ impl Delimiter {
// type. This means that float literals like `1f32` are classified by this type
// as `Int`. Only upon conversion to `ast::LitKind` will such a literal be
// given the `Float` kind.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, Debug, HashStable_Generic)]
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
pub enum LitKind {
Bool, // AST only, must never appear in a `Token`
Byte,
@ -203,7 +179,7 @@ pub enum LitKind {
}
/// A literal token.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, Debug, HashStable_Generic)]
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
pub struct Lit {
pub kind: LitKind,
pub symbol: Symbol,
@ -349,7 +325,7 @@ fn ident_can_begin_type(name: Symbol, span: Span, is_raw: IdentIsRaw) -> bool {
.contains(&name)
}
#[derive(PartialEq, Eq, Encodable, Decodable, Hash, Debug, Copy, Clone, HashStable_Generic)]
#[derive(PartialEq, Encodable, Decodable, Debug, Copy, Clone, HashStable_Generic)]
pub enum IdentIsRaw {
No,
Yes,
@ -376,7 +352,7 @@ impl From<bool> for IdentIsRaw {
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, Debug, HashStable_Generic)]
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
pub enum TokenKind {
/* Expression-operator symbols. */
/// `=`
@ -526,7 +502,7 @@ pub enum TokenKind {
Eof,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, Debug, HashStable_Generic)]
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
pub struct Token {
pub kind: TokenKind,
pub span: Span,
@ -625,12 +601,12 @@ impl TokenKind {
}
impl Token {
pub const fn new(kind: TokenKind, span: Span) -> Self {
pub fn new(kind: TokenKind, span: Span) -> Self {
Token { kind, span }
}
/// Some token that will be thrown away later.
pub const fn dummy() -> Self {
pub fn dummy() -> Self {
Token::new(TokenKind::Question, DUMMY_SP)
}

View file

@ -5,7 +5,6 @@
//! which are themselves a single [`Token`] or a `Delimited` subsequence of tokens.
use std::borrow::Cow;
use std::hash::Hash;
use std::ops::Range;
use std::sync::Arc;
use std::{cmp, fmt, iter, mem};
@ -23,7 +22,7 @@ use crate::token::{self, Delimiter, Token, TokenKind};
use crate::{AttrVec, Attribute};
/// Part of a `TokenStream`.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Encodable, Decodable, HashStable_Generic)]
#[derive(Debug, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
pub enum TokenTree {
/// A single token. Should never be `OpenDelim` or `CloseDelim`, because
/// delimiters are implicitly represented by `Delimited`.
@ -354,13 +353,7 @@ fn make_attr_token_stream(
FrameData { open_delim_sp: Some((delim, span, spacing)), inner: vec![] },
));
} else if let Some(delim) = kind.close_delim() {
// If there's no matching opening delimiter, the token stream is malformed,
// likely due to a improper delimiter positions in the source code.
// It's not delimiter mismatch, and lexer can not detect it, so we just ignore it here.
let Some(frame) = stack_rest.pop() else {
return AttrTokenStream::new(stack_top.inner);
};
let frame_data = mem::replace(&mut stack_top, frame);
let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap());
let (open_delim, open_sp, open_spacing) = frame_data.open_delim_sp.unwrap();
assert!(
open_delim.eq_ignoring_invisible_origin(&delim),
@ -545,7 +538,7 @@ pub struct AttrsTarget {
/// compound token. Used for conversions to `proc_macro::Spacing`. Also used to
/// guide pretty-printing, which is where the `JointHidden` value (which isn't
/// part of `proc_macro::Spacing`) comes in useful.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Encodable, Decodable, HashStable_Generic)]
#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
pub enum Spacing {
/// The token cannot join with the following token to form a compound
/// token.
@ -602,7 +595,7 @@ pub enum Spacing {
}
/// A `TokenStream` is an abstract sequence of tokens, organized into [`TokenTree`]s.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Encodable, Decodable)]
#[derive(Clone, Debug, Default, Encodable, Decodable)]
pub struct TokenStream(pub(crate) Arc<Vec<TokenTree>>);
impl TokenStream {
@ -818,6 +811,14 @@ impl TokenStream {
}
}
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>>())
@ -969,8 +970,7 @@ impl TokenCursor {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Encodable, Decodable, HashStable_Generic, Walkable)]
#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)]
pub struct DelimSpan {
pub open: Span,
pub close: Span,
@ -994,7 +994,7 @@ impl DelimSpan {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable, HashStable_Generic)]
#[derive(Copy, Clone, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
pub struct DelimSpacing {
pub open: Spacing,
pub close: Spacing,

View file

@ -366,7 +366,6 @@ macro_rules! common_visitor_and_walkers {
crate::token::LitKind,
crate::tokenstream::LazyAttrTokenStream,
crate::tokenstream::TokenStream,
EarlyParsedAttribute,
Movability,
Mutability,
Pinnedness,
@ -394,7 +393,6 @@ macro_rules! common_visitor_and_walkers {
ThinVec<Pat>,
ThinVec<Box<Ty>>,
ThinVec<TyPat>,
ThinVec<EiiImpl>,
);
// This macro generates `impl Visitable` and `impl MutVisitable` that forward to `Walkable`
@ -417,7 +415,6 @@ macro_rules! common_visitor_and_walkers {
UnsafeBinderCastKind,
BinOpKind,
BlockCheckMode,
MgcaDisambiguation,
BorrowKind,
BoundAsyncness,
BoundConstness,
@ -425,9 +422,8 @@ macro_rules! common_visitor_and_walkers {
ByRef,
Closure,
Const,
ConstBlockItem,
ConstItem,
ConstItemRhsKind,
ConstItemRhs,
Defaultness,
Delegation,
DelegationMac,
@ -459,7 +455,6 @@ macro_rules! common_visitor_and_walkers {
ModSpans,
MutTy,
NormalAttr,
AttrItemKind,
Parens,
ParenthesizedArgs,
PatFieldsRest,
@ -490,8 +485,6 @@ macro_rules! common_visitor_and_walkers {
WhereEqPredicate,
WhereRegionPredicate,
YieldKind,
EiiDecl,
EiiImpl,
);
/// Each method of this trait is a hook to be potentially
@ -826,8 +819,6 @@ macro_rules! common_visitor_and_walkers {
visit_visitable!($($mut)? vis, use_tree),
ItemKind::Static(item) =>
visit_visitable!($($mut)? vis, item),
ItemKind::ConstBlock(item) =>
visit_visitable!($($mut)? vis, item),
ItemKind::Const(item) =>
visit_visitable!($($mut)? vis, item),
ItemKind::Mod(safety, ident, mod_kind) =>
@ -928,13 +919,13 @@ macro_rules! common_visitor_and_walkers {
_ctxt,
// Visibility is visited as a part of the item.
_vis,
Fn { defaultness, ident, sig, generics, contract, body, define_opaque, eii_impls },
Fn { defaultness, ident, sig, generics, contract, body, define_opaque },
) => {
let FnSig { header, decl, span } = sig;
visit_visitable!($($mut)? vis,
defaultness, ident, header, generics, decl,
contract, body, span, define_opaque, eii_impls
);
contract, body, span, define_opaque
)
}
FnKind::Closure(binder, coroutine_kind, decl, body) =>
visit_visitable!($($mut)? vis, binder, coroutine_kind, decl, body),
@ -1057,8 +1048,8 @@ macro_rules! common_visitor_and_walkers {
visit_visitable!($($mut)? vis, kind),
ExprKind::Try(subexpression) =>
visit_visitable!($($mut)? vis, subexpression),
ExprKind::TryBlock(body, optional_type) =>
visit_visitable!($($mut)? vis, body, optional_type),
ExprKind::TryBlock(body) =>
visit_visitable!($($mut)? vis, body),
ExprKind::Lit(token) =>
visit_visitable!($($mut)? vis, token),
ExprKind::IncludedBytes(bytes) =>

View file

@ -15,6 +15,7 @@ rustc_attr_parsing = { path = "../rustc_attr_parsing" }
rustc_data_structures = { path = "../rustc_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_index = { path = "../rustc_index" }
rustc_macros = { path = "../rustc_macros" }

View file

@ -0,0 +1,188 @@
ast_lowering_abi_specified_multiple_times =
`{$prev_name}` ABI specified multiple times
.label = previously specified here
.note = these ABIs are equivalent on the current target
ast_lowering_arbitrary_expression_in_pattern =
arbitrary expressions aren't allowed in patterns
.pattern_from_macro_note = the `expr` fragment specifier forces the metavariable's content to be an expression
ast_lowering_argument = argument
ast_lowering_assoc_ty_binding_in_dyn =
associated type bounds are not allowed in `dyn` types
.suggestion = use `impl Trait` to introduce a type instead
ast_lowering_assoc_ty_parentheses =
parenthesized generic arguments cannot be used in associated type constraints
ast_lowering_async_bound_not_on_trait =
`async` bound modifier only allowed on trait, not `{$descr}`
ast_lowering_async_bound_only_for_fn_traits =
`async` bound modifier only allowed on `Fn`/`FnMut`/`FnOnce` traits
ast_lowering_async_coroutines_not_supported =
`async` coroutines are not yet supported
ast_lowering_att_syntax_only_x86 =
the `att_syntax` option is only supported on x86
ast_lowering_await_only_in_async_fn_and_blocks =
`await` is only allowed inside `async` functions and blocks
.label = only allowed inside `async` functions and blocks
ast_lowering_bad_return_type_notation_inputs =
argument types not allowed with return type notation
.suggestion = remove the input types
ast_lowering_bad_return_type_notation_needs_dots = return type notation arguments must be elided with `..`
.suggestion = use the correct syntax by adding `..` to the arguments
ast_lowering_bad_return_type_notation_output =
return type not allowed with return type notation
ast_lowering_bad_return_type_notation_output_suggestion = use the right argument notation and remove the return type
ast_lowering_bad_return_type_notation_position = return type notation not allowed in this position yet
ast_lowering_clobber_abi_not_supported =
`clobber_abi` is not supported on this target
ast_lowering_closure_cannot_be_static = closures cannot be static
ast_lowering_coroutine_too_many_parameters =
too many parameters for a coroutine (expected 0 or 1 parameters)
ast_lowering_default_field_in_tuple = default fields are not supported in tuple structs
.label = default fields are only supported on structs
ast_lowering_does_not_support_modifiers =
the `{$class_name}` register class does not support template modifiers
ast_lowering_extra_double_dot =
`..` can only be used once per {$ctx} pattern
.label = can only be used once per {$ctx} pattern
ast_lowering_functional_record_update_destructuring_assignment =
functional record updates are not allowed in destructuring assignments
.suggestion = consider removing the trailing pattern
ast_lowering_generic_param_default_in_binder =
defaults for generic parameters are not allowed in `for<...>` binders
ast_lowering_generic_type_with_parentheses =
parenthesized type parameters may only be used with a `Fn` trait
.label = only `Fn` traits may use parentheses
ast_lowering_inclusive_range_with_no_end = inclusive range with no end
ast_lowering_inline_asm_unsupported_target =
inline assembly is unsupported on this target
ast_lowering_invalid_abi =
invalid ABI: found `{$abi}`
.label = invalid ABI
.note = invoke `{$command}` for a full list of supported calling conventions
ast_lowering_invalid_abi_clobber_abi =
invalid ABI for `clobber_abi`
.note = the following ABIs are supported on this target: {$supported_abis}
ast_lowering_invalid_abi_suggestion = there's a similarly named valid ABI `{$suggestion}`
ast_lowering_invalid_asm_template_modifier_const =
asm template modifiers are not allowed for `const` arguments
ast_lowering_invalid_asm_template_modifier_label =
asm template modifiers are not allowed for `label` arguments
ast_lowering_invalid_asm_template_modifier_reg_class =
invalid asm template modifier for this register class
ast_lowering_invalid_asm_template_modifier_sym =
asm template modifiers are not allowed for `sym` arguments
ast_lowering_invalid_legacy_const_generic_arg =
invalid argument to a legacy const generic: cannot have const blocks, closures, async blocks or items
ast_lowering_invalid_legacy_const_generic_arg_suggestion =
try using a const generic argument instead
ast_lowering_invalid_register =
invalid register `{$reg}`: {$error}
ast_lowering_invalid_register_class =
invalid register class `{$reg_class}`: unknown register class
.note = the following register classes are supported on this target: {$supported_register_classes}
ast_lowering_match_arm_with_no_body =
`match` arm with no body
.suggestion = add a body after the pattern
ast_lowering_misplaced_double_dot =
`..` patterns are not allowed here
.note = only allowed in tuple, tuple struct, and slice patterns
ast_lowering_misplaced_impl_trait =
`impl Trait` is not allowed in {$position}
.note = `impl Trait` is only allowed in arguments and return types of functions and methods
ast_lowering_never_pattern_with_body =
a never pattern is always unreachable
.label = this will never be executed
.suggestion = remove this expression
ast_lowering_never_pattern_with_guard =
a guard on a never pattern will never be run
.suggestion = remove this guard
ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed in argument-position `impl Trait`
ast_lowering_previously_used_here = previously used here
ast_lowering_register1 = register `{$reg1_name}`
ast_lowering_register2 = register `{$reg2_name}`
ast_lowering_register_class_only_clobber =
register class `{$reg_class_name}` can only be used as a clobber, not as an input or output
ast_lowering_register_class_only_clobber_stable =
register class `{$reg_class_name}` can only be used as a clobber in stable
ast_lowering_register_conflict =
register `{$reg1_name}` conflicts with register `{$reg2_name}`
.help = use `lateout` instead of `out` to avoid conflict
ast_lowering_remove_parentheses = remove these parentheses
ast_lowering_sub_tuple_binding =
`{$ident_name} @` is not allowed in a {$ctx}
.label = this is only allowed in slice patterns
.help = remove this and bind each tuple field independently
ast_lowering_sub_tuple_binding_suggestion = if you don't need to use the contents of {$ident}, discard the tuple's remaining fields
ast_lowering_support_modifiers =
the `{$class_name}` register class supports the following template modifiers: {$modifiers}
ast_lowering_template_modifier = template modifier
ast_lowering_this_not_async = this is not `async`
ast_lowering_underscore_expr_lhs_assign =
in expressions, `_` can only be used on the left-hand side of an assignment
.label = `_` not allowed here
ast_lowering_union_default_field_values = unions cannot have default field values
ast_lowering_unstable_inline_assembly = inline assembly is not stable yet on this architecture
ast_lowering_unstable_inline_assembly_label_operand_with_outputs =
using both label and output operands for inline assembly is unstable
ast_lowering_unstable_may_unwind = the `may_unwind` option is unstable
ast_lowering_use_angle_brackets = use angle brackets instead
ast_lowering_yield = yield syntax is experimental
ast_lowering_yield_in_closure =
`yield` can only be used in `#[coroutine]` closures, or `gen` blocks
.suggestion = use `#[coroutine]` to make this closure a coroutine

View file

@ -3,7 +3,6 @@ use std::fmt::Write;
use rustc_ast::*;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_errors::msg;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_session::parse::feature_err;
@ -20,7 +19,8 @@ use super::errors::{
RegisterConflict,
};
use crate::{
AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, ParamMode, ResolverAstLoweringExt,
AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, ParamMode,
ResolverAstLoweringExt, fluent_generated as fluent,
};
impl<'a, 'hir> LoweringContext<'a, 'hir> {
@ -51,23 +51,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
| asm::InlineAsmArch::LoongArch32
| asm::InlineAsmArch::LoongArch64
| asm::InlineAsmArch::S390x
| asm::InlineAsmArch::PowerPC
| asm::InlineAsmArch::PowerPC64
);
if !is_stable
&& !self.tcx.features().asm_experimental_arch()
&& sp
.ctxt()
.outer_expn_data()
.allow_internal_unstable
.filter(|features| features.contains(&sym::asm_experimental_arch))
.is_none()
{
if !is_stable && !self.tcx.features().asm_experimental_arch() {
feature_err(
&self.tcx.sess,
sym::asm_experimental_arch,
sp,
msg!("inline assembly is not stable yet on this architecture"),
fluent::ast_lowering_unstable_inline_assembly,
)
.emit();
}
@ -84,7 +74,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&self.tcx.sess,
sym::asm_unwind,
sp,
msg!("the `may_unwind` option is unstable"),
fluent::ast_lowering_unstable_may_unwind,
)
.emit();
}
@ -499,7 +489,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
sess,
sym::asm_goto_with_outputs,
*op_sp,
msg!("using both label and output operands for inline assembly is unstable"),
fluent::ast_lowering_unstable_inline_assembly_label_operand_with_outputs,
)
.emit();
}

View file

@ -98,7 +98,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// Let statements are allowed to have impl trait in bindings.
let super_ = l.super_.map(|span| self.lower_span(span));
let ty = l.ty.as_ref().map(|t| {
self.lower_ty_alloc(t, self.impl_trait_in_bindings_ctxt(ImplTraitPosition::Variable))
self.lower_ty(t, self.impl_trait_in_bindings_ctxt(ImplTraitPosition::Variable))
});
let init = l.kind.init().map(|init| self.lower_expr(init));
let hir_id = self.lower_node_id(l.id);

View file

@ -23,7 +23,7 @@ impl<'a, 'hir> LoweringContext<'a, '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_decls(contract);
let contract_decls = self.lower_stmts(&contract.declarations).0;
match (&contract.requires, &contract.ensures) {
(Some(req), Some(ens)) => {
@ -124,18 +124,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
fn lower_decls(&mut self, contract: &rustc_ast::FnContract) -> &'hir [rustc_hir::Stmt<'hir>] {
let (decls, decls_tail) = self.lower_stmts(&contract.declarations);
if let Some(e) = decls_tail {
// include the tail expression in the declaration statements
let tail = self.stmt_expr(e.span, *e);
self.arena.alloc_from_iter(decls.into_iter().map(|d| *d).chain([tail].into_iter()))
} else {
decls
}
}
/// 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);

View file

@ -43,21 +43,15 @@ use hir::def::{DefKind, PartialRes, Res};
use hir::{BodyId, HirId};
use rustc_abi::ExternAbi;
use rustc_ast::*;
use rustc_attr_parsing::{AttributeParser, ShouldEmit};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::Target;
use rustc_hir::attrs::{AttributeKind, InlineAttr};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::def_id::DefId;
use rustc_middle::span_bug;
use rustc_middle::ty::{Asyncness, DelegationAttrs, DelegationFnSigAttrs, ResolverAstLowering};
use rustc_middle::ty::{Asyncness, ResolverAstLowering};
use rustc_span::symbol::kw;
use rustc_span::{DUMMY_SP, Ident, Span, Symbol};
use smallvec::SmallVec;
use rustc_span::{Ident, Span, Symbol};
use {rustc_ast as ast, rustc_hir as hir};
use super::{GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode};
use crate::errors::{CycleInDelegationSignatureResolution, UnresolvedDelegationCallee};
use crate::{AllowReturnTypeNotation, ImplTraitPosition, ResolverAstLoweringExt};
pub(crate) struct DelegationResults<'hir> {
@ -67,68 +61,6 @@ pub(crate) struct DelegationResults<'hir> {
pub generics: &'hir hir::Generics<'hir>,
}
struct AttrAdditionInfo {
pub equals: fn(&hir::Attribute) -> bool,
pub kind: AttrAdditionKind,
}
enum AttrAdditionKind {
Default { factory: fn(Span) -> hir::Attribute },
Inherit { flag: DelegationFnSigAttrs, factory: fn(Span, &hir::Attribute) -> hir::Attribute },
}
const PARENT_ID: hir::ItemLocalId = hir::ItemLocalId::ZERO;
static ATTRS_ADDITIONS: &[AttrAdditionInfo] = &[
AttrAdditionInfo {
equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::MustUse { .. })),
kind: AttrAdditionKind::Inherit {
factory: |span, original_attr| {
let reason = match original_attr {
hir::Attribute::Parsed(AttributeKind::MustUse { reason, .. }) => *reason,
_ => None,
};
hir::Attribute::Parsed(AttributeKind::MustUse { span, reason })
},
flag: DelegationFnSigAttrs::MUST_USE,
},
},
AttrAdditionInfo {
equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::Inline(..))),
kind: AttrAdditionKind::Default {
factory: |span| hir::Attribute::Parsed(AttributeKind::Inline(InlineAttr::Hint, span)),
},
},
];
type DelegationIdsVec = SmallVec<[DefId; 1]>;
// As delegations can now refer to another delegation, we have a delegation path
// of the following type: reuse (current delegation) <- reuse (delegee_id) <- ... <- reuse <- function (root_function_id).
// In its most basic and widely used form: reuse (current delegation) <- function (delegee_id, root_function_id)
struct DelegationIds {
path: DelegationIdsVec,
}
impl DelegationIds {
fn new(path: DelegationIdsVec) -> Self {
assert!(!path.is_empty());
Self { path }
}
// Id of the first function in (non)local crate that is being reused
fn root_function_id(&self) -> DefId {
*self.path.last().expect("Ids vector can't be empty")
}
// Id of the first definition which is being reused,
// can be either function, in this case `root_id == delegee_id`, or other delegation
fn delegee_id(&self) -> DefId {
*self.path.first().expect("Ids vector can't be empty")
}
}
impl<'hir> LoweringContext<'_, 'hir> {
fn is_method(&self, def_id: DefId, span: Span) -> bool {
match self.tcx.def_kind(def_id) {
@ -149,50 +81,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
&mut self,
delegation: &Delegation,
item_id: NodeId,
is_in_trait_impl: bool,
) -> DelegationResults<'hir> {
let span = self.lower_span(delegation.path.segments.last().unwrap().ident.span);
// Delegation can be unresolved in illegal places such as function bodies in extern blocks (see #151356)
let ids = if let Some(delegation_info) =
self.resolver.delegation_infos.get(&self.local_def_id(item_id))
{
self.get_delegation_ids(delegation_info.resolution_node, span)
} else {
return self.generate_delegation_error(
self.dcx().span_delayed_bug(
span,
format!("LoweringContext: the delegation {:?} is unresolved", item_id),
),
span,
delegation,
);
};
match ids {
Ok(ids) => {
self.add_attrs_if_needed(span, &ids);
let delegee_id = ids.delegee_id();
let root_function_id = ids.root_function_id();
// `is_method` is used to choose the name of the first parameter (`self` or `arg0`),
// if the original function is not a method (without `self`), then it can not be added
// during chain of reuses, so we use `root_function_id` here
let is_method = self.is_method(root_function_id, span);
// Here we use `root_function_id` as we can not get params information out of potential delegation reuse,
// we need a function to extract this information
let (param_count, c_variadic) = self.param_count(root_function_id);
// Here we use `delegee_id`, as this id will then be used to calculate parent for generics
// inheritance, and we want this id to point on a delegee, not on the original
// function (see https://github.com/rust-lang/rust/issues/150152#issuecomment-3674834654)
let decl = self.lower_delegation_decl(delegee_id, param_count, c_variadic, span);
// Here we pass `root_function_id` as we want to inherit signature (including consts, async)
// from the root function that started delegation
let sig = self.lower_delegation_sig(root_function_id, decl, span);
let sig_id = self.get_delegation_sig_id(item_id, delegation.id, span, is_in_trait_impl);
match sig_id {
Ok(sig_id) => {
let is_method = self.is_method(sig_id, span);
let (param_count, c_variadic) = self.param_count(sig_id);
let decl = self.lower_delegation_decl(sig_id, param_count, c_variadic, span);
let sig = self.lower_delegation_sig(sig_id, decl, span);
let body_id = self.lower_delegation_body(delegation, is_method, param_count, span);
let ident = self.lower_ident(delegation.ident);
let generics = self.lower_delegation_generics(span);
@ -202,163 +100,26 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
fn add_attrs_if_needed(&mut self, span: Span, ids: &DelegationIds) {
let new_attrs =
self.create_new_attrs(ATTRS_ADDITIONS, span, ids, self.attrs.get(&PARENT_ID));
if new_attrs.is_empty() {
return;
}
let new_arena_allocated_attrs = match self.attrs.get(&PARENT_ID) {
Some(existing_attrs) => self.arena.alloc_from_iter(
existing_attrs.iter().map(|a| a.clone()).chain(new_attrs.into_iter()),
),
None => self.arena.alloc_from_iter(new_attrs.into_iter()),
};
self.attrs.insert(PARENT_ID, new_arena_allocated_attrs);
}
fn create_new_attrs(
fn get_delegation_sig_id(
&self,
candidate_additions: &[AttrAdditionInfo],
item_id: NodeId,
path_id: NodeId,
span: Span,
ids: &DelegationIds,
existing_attrs: Option<&&[hir::Attribute]>,
) -> Vec<hir::Attribute> {
let defs_orig_attrs = ids
.path
.iter()
.map(|def_id| (*def_id, self.parse_local_original_attrs(*def_id)))
.collect::<Vec<_>>();
candidate_additions
.iter()
.filter_map(|addition_info| {
if let Some(existing_attrs) = existing_attrs
&& existing_attrs
.iter()
.any(|existing_attr| (addition_info.equals)(existing_attr))
{
return None;
is_in_trait_impl: bool,
) -> Result<DefId, ErrorGuaranteed> {
let sig_id = if is_in_trait_impl { item_id } else { path_id };
self.get_resolution_id(sig_id, span)
}
match addition_info.kind {
AttrAdditionKind::Default { factory } => Some(factory(span)),
AttrAdditionKind::Inherit { flag, factory } => {
for (def_id, orig_attrs) in &defs_orig_attrs {
let original_attr = match def_id.as_local() {
Some(local_id) => self
.get_attrs(local_id)
.flags
.contains(flag)
.then(|| {
orig_attrs
.as_ref()
.map(|attrs| {
attrs.iter().find(|base_attr| {
(addition_info.equals)(base_attr)
})
})
.flatten()
})
.flatten(),
None => self
.tcx
.get_all_attrs(*def_id)
.iter()
.find(|base_attr| (addition_info.equals)(base_attr)),
};
if let Some(original_attr) = original_attr {
return Some(factory(span, original_attr));
}
}
None
}
}
})
.collect::<Vec<_>>()
}
fn parse_local_original_attrs(&self, def_id: DefId) -> Option<Vec<hir::Attribute>> {
if let Some(local_id) = def_id.as_local() {
let attrs = &self.get_attrs(local_id).to_inherit;
if !attrs.is_empty() {
return Some(AttributeParser::parse_limited_all(
self.tcx.sess,
attrs,
None,
Target::Fn,
DUMMY_SP,
DUMMY_NODE_ID,
Some(self.tcx.features()),
ShouldEmit::Nothing,
));
}
}
None
}
fn get_attrs(&self, local_id: LocalDefId) -> &DelegationAttrs {
// local_id can correspond either to a function or other delegation
if let Some(fn_sig) = self.resolver.delegation_fn_sigs.get(&local_id) {
&fn_sig.attrs
} else {
&self.resolver.delegation_infos[&local_id].attrs
}
}
fn get_delegation_ids(
&self,
mut node_id: NodeId,
span: Span,
) -> Result<DelegationIds, ErrorGuaranteed> {
let mut visited: FxHashSet<NodeId> = Default::default();
let mut path: DelegationIdsVec = Default::default();
loop {
visited.insert(node_id);
let Some(def_id) = self.get_resolution_id(node_id) else {
return Err(self.tcx.dcx().span_delayed_bug(
fn get_resolution_id(&self, node_id: NodeId, span: Span) -> Result<DefId, ErrorGuaranteed> {
let def_id =
self.resolver.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id());
def_id.ok_or_else(|| {
self.tcx.dcx().span_delayed_bug(
span,
format!(
"LoweringContext: couldn't resolve node {:?} in delegation item",
node_id
),
));
};
path.push(def_id);
// If def_id is in local crate and it corresponds to another delegation
// it means that we refer to another delegation as a callee, so in order to obtain
// a signature DefId we obtain NodeId of the callee delegation and try to get signature from it.
if let Some(local_id) = def_id.as_local()
&& let Some(delegation_info) = self.resolver.delegation_infos.get(&local_id)
{
node_id = delegation_info.resolution_node;
if visited.contains(&node_id) {
// We encountered a cycle in the resolution, or delegation callee refers to non-existent
// entity, in this case emit an error.
return Err(match visited.len() {
1 => self.dcx().emit_err(UnresolvedDelegationCallee { span }),
_ => self.dcx().emit_err(CycleInDelegationSignatureResolution { span }),
});
}
} else {
return Ok(DelegationIds::new(path));
}
}
}
fn get_resolution_id(&self, node_id: NodeId) -> Option<DefId> {
self.resolver.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id())
format!("LoweringContext: couldn't resolve node {:?} in delegation item", node_id),
)
})
}
fn lower_delegation_generics(&mut self, span: Span) -> &'hir hir::Generics<'hir> {
@ -372,14 +133,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
// Function parameter count, including C variadic `...` if present.
fn param_count(&self, def_id: DefId) -> (usize, bool /*c_variadic*/) {
if let Some(local_sig_id) = def_id.as_local() {
fn param_count(&self, sig_id: DefId) -> (usize, bool /*c_variadic*/) {
if let Some(local_sig_id) = sig_id.as_local() {
// Map may be filled incorrectly due to recursive delegation.
// Error will be emitted later during HIR ty lowering.
match self.resolver.delegation_fn_sigs.get(&local_sig_id) {
Some(sig) => (sig.param_count, sig.c_variadic),
None => (0, false),
}
} else {
let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder();
let sig = self.tcx.fn_sig(sig_id).skip_binder().skip_binder();
(sig.inputs().len() + usize::from(sig.c_variadic), sig.c_variadic)
}
}
@ -429,9 +192,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// We are not forwarding the attributes, as the delegation fn sigs are collected on the ast,
// and here we need the hir attributes.
let default_safety =
if sig.attrs.flags.contains(DelegationFnSigAttrs::TARGET_FEATURE)
|| self.tcx.def_kind(parent) == DefKind::ForeignMod
{
if sig.target_feature || self.tcx.def_kind(parent) == DefKind::ForeignMod {
hir::Safety::Unsafe
} else {
hir::Safety::Safe
@ -590,8 +351,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
delegation.path.segments.iter().rev().skip(1).any(|segment| segment.args.is_some());
let call = if self
.get_resolution_id(delegation.id)
.map(|def_id| self.is_method(def_id, span))
.get_resolution_id(delegation.id, span)
.and_then(|def_id| Ok(self.is_method(def_id, span)))
.unwrap_or_default()
&& delegation.qself.is_none()
&& !has_generic_args

View file

@ -4,17 +4,17 @@ use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Ident, Span, Symbol};
#[derive(Diagnostic)]
#[diag("parenthesized type parameters may only be used with a `Fn` trait", code = E0214)]
#[diag(ast_lowering_generic_type_with_parentheses, code = E0214)]
pub(crate) struct GenericTypeWithParentheses {
#[primary_span]
#[label("only `Fn` traits may use parentheses")]
#[label]
pub span: Span,
#[subdiagnostic]
pub sub: Option<UseAngleBrackets>,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion("use angle brackets instead", applicability = "maybe-incorrect")]
#[multipart_suggestion(ast_lowering_use_angle_brackets, applicability = "maybe-incorrect")]
pub(crate) struct UseAngleBrackets {
#[suggestion_part(code = "<")]
pub open_param: Span,
@ -23,11 +23,11 @@ pub(crate) struct UseAngleBrackets {
}
#[derive(Diagnostic)]
#[diag("invalid ABI: found `{$abi}`", code = E0703)]
#[note("invoke `{$command}` for a full list of supported calling conventions")]
#[diag(ast_lowering_invalid_abi, code = E0703)]
#[note]
pub(crate) struct InvalidAbi {
#[primary_span]
#[label("invalid ABI")]
#[label]
pub span: Span,
pub abi: Symbol,
pub command: String,
@ -36,16 +36,16 @@ pub(crate) struct InvalidAbi {
}
#[derive(Diagnostic)]
#[diag("default fields are not supported in tuple structs")]
#[diag(ast_lowering_default_field_in_tuple)]
pub(crate) struct TupleStructWithDefault {
#[primary_span]
#[label("default fields are only supported on structs")]
#[label]
pub span: Span,
}
#[derive(Subdiagnostic)]
#[suggestion(
"there's a similarly named valid ABI `{$suggestion}`",
ast_lowering_invalid_abi_suggestion,
code = "\"{suggestion}\"",
applicability = "maybe-incorrect",
style = "verbose"
@ -57,7 +57,7 @@ pub(crate) struct InvalidAbiSuggestion {
}
#[derive(Diagnostic)]
#[diag("parenthesized generic arguments cannot be used in associated type constraints")]
#[diag(ast_lowering_assoc_ty_parentheses)]
pub(crate) struct AssocTyParentheses {
#[primary_span]
pub span: Span,
@ -67,12 +67,12 @@ pub(crate) struct AssocTyParentheses {
#[derive(Subdiagnostic)]
pub(crate) enum AssocTyParenthesesSub {
#[multipart_suggestion("remove these parentheses")]
#[multipart_suggestion(ast_lowering_remove_parentheses)]
Empty {
#[suggestion_part(code = "")]
parentheses_span: Span,
},
#[multipart_suggestion("use angle brackets instead")]
#[multipart_suggestion(ast_lowering_use_angle_brackets)]
NotEmpty {
#[suggestion_part(code = "<")]
open_param: Span,
@ -82,8 +82,8 @@ pub(crate) enum AssocTyParenthesesSub {
}
#[derive(Diagnostic)]
#[diag("`impl Trait` is not allowed in {$position}", code = E0562)]
#[note("`impl Trait` is only allowed in arguments and return types of functions and methods")]
#[diag(ast_lowering_misplaced_impl_trait, code = E0562)]
#[note]
pub(crate) struct MisplacedImplTrait<'a> {
#[primary_span]
pub span: Span,
@ -91,106 +91,97 @@ pub(crate) struct MisplacedImplTrait<'a> {
}
#[derive(Diagnostic)]
#[diag("associated type bounds are not allowed in `dyn` types")]
#[diag(ast_lowering_assoc_ty_binding_in_dyn)]
pub(crate) struct MisplacedAssocTyBinding {
#[primary_span]
pub span: Span,
#[suggestion(
"use `impl Trait` to introduce a type instead",
code = " = impl",
applicability = "maybe-incorrect",
style = "verbose"
)]
#[suggestion(code = " = impl", applicability = "maybe-incorrect", style = "verbose")]
pub suggestion: Option<Span>,
}
#[derive(Diagnostic)]
#[diag("in expressions, `_` can only be used on the left-hand side of an assignment")]
#[diag(ast_lowering_underscore_expr_lhs_assign)]
pub(crate) struct UnderscoreExprLhsAssign {
#[primary_span]
#[label("`_` not allowed here")]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`await` is only allowed inside `async` functions and blocks", code = E0728)]
#[diag(ast_lowering_await_only_in_async_fn_and_blocks, code = E0728)]
pub(crate) struct AwaitOnlyInAsyncFnAndBlocks {
#[primary_span]
#[label("only allowed inside `async` functions and blocks")]
#[label]
pub await_kw_span: Span,
#[label("this is not `async`")]
#[label(ast_lowering_this_not_async)]
pub item_span: Option<Span>,
}
#[derive(Diagnostic)]
#[diag("too many parameters for a coroutine (expected 0 or 1 parameters)", code = E0628)]
#[diag(ast_lowering_coroutine_too_many_parameters, code = E0628)]
pub(crate) struct CoroutineTooManyParameters {
#[primary_span]
pub fn_decl_span: Span,
}
#[derive(Diagnostic)]
#[diag("closures cannot be static", code = E0697)]
#[diag(ast_lowering_closure_cannot_be_static, code = E0697)]
pub(crate) struct ClosureCannotBeStatic {
#[primary_span]
pub fn_decl_span: Span,
}
#[derive(Diagnostic)]
#[diag("functional record updates are not allowed in destructuring assignments")]
#[diag(ast_lowering_functional_record_update_destructuring_assignment)]
pub(crate) struct FunctionalRecordUpdateDestructuringAssignment {
#[primary_span]
#[suggestion(
"consider removing the trailing pattern",
code = "",
applicability = "machine-applicable"
)]
#[suggestion(code = "", applicability = "machine-applicable")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`async` coroutines are not yet supported", code = E0727)]
#[diag(ast_lowering_async_coroutines_not_supported, code = E0727)]
pub(crate) struct AsyncCoroutinesNotSupported {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("inline assembly is unsupported on this target", code = E0472)]
#[diag(ast_lowering_inline_asm_unsupported_target, code = E0472)]
pub(crate) struct InlineAsmUnsupportedTarget {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("the `att_syntax` option is only supported on x86")]
#[diag(ast_lowering_att_syntax_only_x86)]
pub(crate) struct AttSyntaxOnlyX86 {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`{$prev_name}` ABI specified multiple times")]
#[diag(ast_lowering_abi_specified_multiple_times)]
pub(crate) struct AbiSpecifiedMultipleTimes {
#[primary_span]
pub abi_span: Span,
pub prev_name: Symbol,
#[label("previously specified here")]
#[label]
pub prev_span: Span,
#[note("these ABIs are equivalent on the current target")]
#[note]
pub equivalent: bool,
}
#[derive(Diagnostic)]
#[diag("`clobber_abi` is not supported on this target")]
#[diag(ast_lowering_clobber_abi_not_supported)]
pub(crate) struct ClobberAbiNotSupported {
#[primary_span]
pub abi_span: Span,
}
#[derive(Diagnostic)]
#[note("the following ABIs are supported on this target: {$supported_abis}")]
#[diag("invalid ABI for `clobber_abi`")]
#[note]
#[diag(ast_lowering_invalid_abi_clobber_abi)]
pub(crate) struct InvalidAbiClobberAbi {
#[primary_span]
pub abi_span: Span,
@ -198,7 +189,7 @@ pub(crate) struct InvalidAbiClobberAbi {
}
#[derive(Diagnostic)]
#[diag("invalid register `{$reg}`: {$error}")]
#[diag(ast_lowering_invalid_register)]
pub(crate) struct InvalidRegister<'a> {
#[primary_span]
pub op_span: Span,
@ -207,10 +198,8 @@ pub(crate) struct InvalidRegister<'a> {
}
#[derive(Diagnostic)]
#[note(
"the following register classes are supported on this target: {$supported_register_classes}"
)]
#[diag("invalid register class `{$reg_class}`: unknown register class")]
#[note]
#[diag(ast_lowering_invalid_register_class)]
pub(crate) struct InvalidRegisterClass {
#[primary_span]
pub op_span: Span,
@ -219,12 +208,12 @@ pub(crate) struct InvalidRegisterClass {
}
#[derive(Diagnostic)]
#[diag("invalid asm template modifier for this register class")]
#[diag(ast_lowering_invalid_asm_template_modifier_reg_class)]
pub(crate) struct InvalidAsmTemplateModifierRegClass {
#[primary_span]
#[label("template modifier")]
#[label(ast_lowering_template_modifier)]
pub placeholder_span: Span,
#[label("argument")]
#[label(ast_lowering_argument)]
pub op_span: Span,
#[subdiagnostic]
pub sub: InvalidAsmTemplateModifierRegClassSub,
@ -232,48 +221,44 @@ pub(crate) struct InvalidAsmTemplateModifierRegClass {
#[derive(Subdiagnostic)]
pub(crate) enum InvalidAsmTemplateModifierRegClassSub {
#[note(
"the `{$class_name}` register class supports the following template modifiers: {$modifiers}"
)]
#[note(ast_lowering_support_modifiers)]
SupportModifier { class_name: Symbol, modifiers: String },
#[note("the `{$class_name}` register class does not support template modifiers")]
#[note(ast_lowering_does_not_support_modifiers)]
DoesNotSupportModifier { class_name: Symbol },
}
#[derive(Diagnostic)]
#[diag("asm template modifiers are not allowed for `const` arguments")]
#[diag(ast_lowering_invalid_asm_template_modifier_const)]
pub(crate) struct InvalidAsmTemplateModifierConst {
#[primary_span]
#[label("template modifier")]
#[label(ast_lowering_template_modifier)]
pub placeholder_span: Span,
#[label("argument")]
#[label(ast_lowering_argument)]
pub op_span: Span,
}
#[derive(Diagnostic)]
#[diag("asm template modifiers are not allowed for `sym` arguments")]
#[diag(ast_lowering_invalid_asm_template_modifier_sym)]
pub(crate) struct InvalidAsmTemplateModifierSym {
#[primary_span]
#[label("template modifier")]
#[label(ast_lowering_template_modifier)]
pub placeholder_span: Span,
#[label("argument")]
#[label(ast_lowering_argument)]
pub op_span: Span,
}
#[derive(Diagnostic)]
#[diag("asm template modifiers are not allowed for `label` arguments")]
#[diag(ast_lowering_invalid_asm_template_modifier_label)]
pub(crate) struct InvalidAsmTemplateModifierLabel {
#[primary_span]
#[label("template modifier")]
#[label(ast_lowering_template_modifier)]
pub placeholder_span: Span,
#[label("argument")]
#[label(ast_lowering_argument)]
pub op_span: Span,
}
#[derive(Diagnostic)]
#[diag(
"register class `{$reg_class_name}` can only be used as a clobber, not as an input or output"
)]
#[diag(ast_lowering_register_class_only_clobber)]
pub(crate) struct RegisterClassOnlyClobber {
#[primary_span]
pub op_span: Span,
@ -281,7 +266,7 @@ pub(crate) struct RegisterClassOnlyClobber {
}
#[derive(Diagnostic)]
#[diag("register class `{$reg_class_name}` can only be used as a clobber in stable")]
#[diag(ast_lowering_register_class_only_clobber_stable)]
pub(crate) struct RegisterClassOnlyClobberStable {
#[primary_span]
pub op_span: Span,
@ -289,27 +274,27 @@ pub(crate) struct RegisterClassOnlyClobberStable {
}
#[derive(Diagnostic)]
#[diag("register `{$reg1_name}` conflicts with register `{$reg2_name}`")]
#[diag(ast_lowering_register_conflict)]
pub(crate) struct RegisterConflict<'a> {
#[primary_span]
#[label("register `{$reg1_name}`")]
#[label(ast_lowering_register1)]
pub op_span1: Span,
#[label("register `{$reg2_name}`")]
#[label(ast_lowering_register2)]
pub op_span2: Span,
pub reg1_name: &'a str,
pub reg2_name: &'a str,
#[help("use `lateout` instead of `out` to avoid conflict")]
#[help]
pub in_out: Option<Span>,
}
#[derive(Diagnostic)]
#[help("remove this and bind each tuple field independently")]
#[diag("`{$ident_name} @` is not allowed in a {$ctx}")]
#[help]
#[diag(ast_lowering_sub_tuple_binding)]
pub(crate) struct SubTupleBinding<'a> {
#[primary_span]
#[label("this is only allowed in slice patterns")]
#[label]
#[suggestion(
"if you don't need to use the contents of {$ident}, discard the tuple's remaining fields",
ast_lowering_sub_tuple_binding_suggestion,
style = "verbose",
code = "..",
applicability = "maybe-incorrect"
@ -321,67 +306,61 @@ pub(crate) struct SubTupleBinding<'a> {
}
#[derive(Diagnostic)]
#[diag("`..` can only be used once per {$ctx} pattern")]
#[diag(ast_lowering_extra_double_dot)]
pub(crate) struct ExtraDoubleDot<'a> {
#[primary_span]
#[label("can only be used once per {$ctx} pattern")]
#[label]
pub span: Span,
#[label("previously used here")]
#[label(ast_lowering_previously_used_here)]
pub prev_span: Span,
pub ctx: &'a str,
}
#[derive(Diagnostic)]
#[note("only allowed in tuple, tuple struct, and slice patterns")]
#[diag("`..` patterns are not allowed here")]
#[note]
#[diag(ast_lowering_misplaced_double_dot)]
pub(crate) struct MisplacedDoubleDot {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`match` arm with no body")]
#[diag(ast_lowering_match_arm_with_no_body)]
pub(crate) struct MatchArmWithNoBody {
#[primary_span]
pub span: Span,
#[suggestion(
"add a body after the pattern",
code = " => todo!(),",
applicability = "has-placeholders"
)]
#[suggestion(code = " => todo!(),", applicability = "has-placeholders")]
pub suggestion: Span,
}
#[derive(Diagnostic)]
#[diag("a never pattern is always unreachable")]
#[diag(ast_lowering_never_pattern_with_body)]
pub(crate) struct NeverPatternWithBody {
#[primary_span]
#[label("this will never be executed")]
#[suggestion("remove this expression", code = "", applicability = "maybe-incorrect")]
#[label]
#[suggestion(code = "", applicability = "maybe-incorrect")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("a guard on a never pattern will never be run")]
#[diag(ast_lowering_never_pattern_with_guard)]
pub(crate) struct NeverPatternWithGuard {
#[primary_span]
#[suggestion("remove this guard", code = "", applicability = "maybe-incorrect")]
#[suggestion(code = "", applicability = "maybe-incorrect")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("arbitrary expressions aren't allowed in patterns")]
#[diag(ast_lowering_arbitrary_expression_in_pattern)]
pub(crate) struct ArbitraryExpressionInPattern {
#[primary_span]
pub span: Span,
#[note("the `expr` fragment specifier forces the metavariable's content to be an expression")]
#[note(ast_lowering_pattern_from_macro_note)]
pub pattern_from_macro_note: bool,
#[help("use a named `const`-item or an `if`-guard (`x if x == const {\"{ ... }\"}`) instead")]
pub const_block_in_pattern_help: bool,
}
#[derive(Diagnostic)]
#[diag("inclusive range with no end")]
#[diag(ast_lowering_inclusive_range_with_no_end)]
pub(crate) struct InclusiveRangeWithNoEnd {
#[primary_span]
pub span: Span,
@ -389,7 +368,7 @@ pub(crate) struct InclusiveRangeWithNoEnd {
#[derive(Subdiagnostic)]
#[multipart_suggestion(
"use the right argument notation and remove the return type",
ast_lowering_bad_return_type_notation_output_suggestion,
applicability = "machine-applicable",
style = "verbose"
)]
@ -403,36 +382,26 @@ pub(crate) struct RTNSuggestion {
#[derive(Diagnostic)]
pub(crate) enum BadReturnTypeNotation {
#[diag("argument types not allowed with return type notation")]
#[diag(ast_lowering_bad_return_type_notation_inputs)]
Inputs {
#[primary_span]
#[suggestion(
"remove the input types",
code = "(..)",
applicability = "machine-applicable",
style = "verbose"
)]
#[suggestion(code = "(..)", applicability = "machine-applicable", style = "verbose")]
span: Span,
},
#[diag("return type not allowed with return type notation")]
#[diag(ast_lowering_bad_return_type_notation_output)]
Output {
#[primary_span]
span: Span,
#[subdiagnostic]
suggestion: RTNSuggestion,
},
#[diag("return type notation arguments must be elided with `..`")]
#[diag(ast_lowering_bad_return_type_notation_needs_dots)]
NeedsDots {
#[primary_span]
#[suggestion(
"use the correct syntax by adding `..` to the arguments",
code = "(..)",
applicability = "machine-applicable",
style = "verbose"
)]
#[suggestion(code = "(..)", applicability = "machine-applicable", style = "verbose")]
span: Span,
},
#[diag("return type notation not allowed in this position yet")]
#[diag(ast_lowering_bad_return_type_notation_position)]
Position {
#[primary_span]
span: Span,
@ -440,14 +409,14 @@ pub(crate) enum BadReturnTypeNotation {
}
#[derive(Diagnostic)]
#[diag("defaults for generic parameters are not allowed in `for<...>` binders")]
#[diag(ast_lowering_generic_param_default_in_binder)]
pub(crate) struct GenericParamDefaultInBinder {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`async` bound modifier only allowed on trait, not `{$descr}`")]
#[diag(ast_lowering_async_bound_not_on_trait)]
pub(crate) struct AsyncBoundNotOnTrait {
#[primary_span]
pub span: Span,
@ -455,37 +424,30 @@ pub(crate) struct AsyncBoundNotOnTrait {
}
#[derive(Diagnostic)]
#[diag("`async` bound modifier only allowed on `Fn`/`FnMut`/`FnOnce` traits")]
#[diag(ast_lowering_async_bound_only_for_fn_traits)]
pub(crate) struct AsyncBoundOnlyForFnTraits {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`use<...>` precise capturing syntax not allowed in argument-position `impl Trait`")]
#[diag(ast_lowering_no_precise_captures_on_apit)]
pub(crate) struct NoPreciseCapturesOnApit {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`yield` can only be used in `#[coroutine]` closures, or `gen` blocks")]
#[diag(ast_lowering_yield_in_closure)]
pub(crate) struct YieldInClosure {
#[primary_span]
pub span: Span,
#[suggestion(
"use `#[coroutine]` to make this closure a coroutine",
code = "#[coroutine] ",
applicability = "maybe-incorrect",
style = "verbose"
)]
#[suggestion(code = "#[coroutine] ", applicability = "maybe-incorrect", style = "verbose")]
pub suggestion: Option<Span>,
}
#[derive(Diagnostic)]
#[diag(
"invalid argument to a legacy const generic: cannot have const blocks, closures, async blocks or items"
)]
#[diag(ast_lowering_invalid_legacy_const_generic_arg)]
pub(crate) struct InvalidLegacyConstGenericArg {
#[primary_span]
pub span: Span,
@ -495,7 +457,7 @@ pub(crate) struct InvalidLegacyConstGenericArg {
#[derive(Subdiagnostic)]
#[multipart_suggestion(
"try using a const generic argument instead",
ast_lowering_invalid_legacy_const_generic_arg_suggestion,
applicability = "maybe-incorrect"
)]
pub(crate) struct UseConstGenericArg {
@ -508,22 +470,8 @@ pub(crate) struct UseConstGenericArg {
}
#[derive(Diagnostic)]
#[diag("unions cannot have default field values")]
#[diag(ast_lowering_union_default_field_values)]
pub(crate) struct UnionWithDefault {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("failed to resolve delegation callee")]
pub(crate) struct UnresolvedDelegationCallee {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("encountered a cycle during delegation signature resolution")]
pub(crate) struct CycleInDelegationSignatureResolution {
#[primary_span]
pub span: Span,
}

View file

@ -1,11 +1,9 @@
use std::mem;
use std::ops::ControlFlow;
use std::sync::Arc;
use rustc_ast::*;
use rustc_ast_pretty::pprust::expr_to_string;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::msg;
use rustc_hir as hir;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{DefKind, Res};
@ -29,7 +27,7 @@ use super::{
GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt,
};
use crate::errors::{InvalidLegacyConstGenericArg, UseConstGenericArg, YieldInClosure};
use crate::{AllowReturnTypeNotation, FnDeclKind, ImplTraitPosition, TryBlockScope};
use crate::{AllowReturnTypeNotation, FnDeclKind, ImplTraitPosition, fluent_generated};
struct WillCreateDefIdsVisitor {}
@ -113,8 +111,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
ExprKind::Tup(elts) => hir::ExprKind::Tup(self.lower_exprs(elts)),
ExprKind::Call(f, args) => {
if let Some(legacy_args) = self.resolver.legacy_const_generic_args(f, self.tcx)
{
if let Some(legacy_args) = self.resolver.legacy_const_generic_args(f) {
self.lower_legacy_const_generics((**f).clone(), args.clone(), &legacy_args)
} else {
let f = self.lower_expr(f);
@ -157,14 +154,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
ExprKind::Cast(expr, ty) => {
let expr = self.lower_expr(expr);
let ty = self
.lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast));
let ty =
self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast));
hir::ExprKind::Cast(expr, ty)
}
ExprKind::Type(expr, ty) => {
let expr = self.lower_expr(expr);
let ty = self
.lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast));
let ty =
self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast));
hir::ExprKind::Type(expr, ty)
}
ExprKind::AddrOf(k, m, ohs) => {
@ -202,9 +199,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
})
}
ExprKind::TryBlock(body, opt_ty) => {
self.lower_expr_try_block(body, opt_ty.as_deref())
}
ExprKind::TryBlock(body) => self.lower_expr_try_block(body),
ExprKind::Match(expr, arms, kind) => hir::ExprKind::Match(
self.lower_expr(expr),
self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))),
@ -334,7 +329,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
ExprKind::FormatArgs(fmt) => self.lower_format_args(e.span, fmt),
ExprKind::OffsetOf(container, fields) => hir::ExprKind::OffsetOf(
self.lower_ty_alloc(
self.lower_ty(
container,
ImplTraitContext::Disallowed(ImplTraitPosition::OffsetOf),
),
@ -370,10 +365,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
*kind,
self.lower_expr(expr),
ty.as_ref().map(|ty| {
self.lower_ty_alloc(
ty,
ImplTraitContext::Disallowed(ImplTraitPosition::Cast),
)
self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast))
}),
),
@ -492,11 +484,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
arg
};
let anon_const = AnonConst {
id: node_id,
value: const_value,
mgca_disambiguation: MgcaDisambiguation::AnonConst,
};
let anon_const = AnonConst { id: node_id, value: const_value };
generic_args.push(AngleBracketedArg::Arg(GenericArg::Const(anon_const)));
} else {
real_args.push(arg);
@ -574,14 +562,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
/// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_output(<expr>) }`,
/// `try { <stmts>; }` into `{ <stmts>; ::std::ops::Try::from_output(()) }`
/// and save the block id to use it as a break target for desugaring of the `?` operator.
fn lower_expr_try_block(&mut self, body: &Block, opt_ty: Option<&Ty>) -> hir::ExprKind<'hir> {
fn lower_expr_try_block(&mut self, body: &Block) -> hir::ExprKind<'hir> {
let body_hir_id = self.lower_node_id(body.id);
let new_scope = if opt_ty.is_some() {
TryBlockScope::Heterogeneous(body_hir_id)
} else {
TryBlockScope::Homogeneous(body_hir_id)
};
let whole_block = self.with_try_block_scope(new_scope, |this| {
self.with_catch_scope(body_hir_id, |this| {
let mut block = this.lower_block_noalloc(body_hir_id, body, true);
// Final expression of the block (if present) or `()` with span at the end of block
@ -615,16 +598,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
ok_wrapped_span,
));
this.arena.alloc(block)
});
if let Some(ty) = opt_ty {
let ty = self.lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Path));
let block_expr = self.arena.alloc(self.expr_block(whole_block));
hir::ExprKind::Type(block_expr, ty)
} else {
hir::ExprKind::Block(whole_block, None)
}
hir::ExprKind::Block(this.arena.alloc(block), None)
})
}
fn wrap_in_try_constructor(
@ -966,14 +941,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::Break(this.lower_loop_destination(None), Some(x_expr));
this.arena.alloc(this.expr(gen_future_span, expr_break))
});
self.arm(ready_pat, break_x, span)
self.arm(ready_pat, break_x)
};
// `::std::task::Poll::Pending => {}`
let pending_arm = {
let pending_pat = self.pat_lang_item_variant(span, hir::LangItem::PollPending, &[]);
let empty_block = self.expr_block_empty(span);
self.arm(pending_pat, empty_block, span)
self.arm(pending_pat, empty_block)
};
let inner_match_stmt = {
@ -1027,7 +1002,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
});
// mut __awaitee => loop { ... }
let awaitee_arm = self.arm(awaitee_pat, loop_expr, span);
let awaitee_arm = self.arm(awaitee_pat, loop_expr);
// `match ::std::future::IntoFuture::into_future(<expr>) { ... }`
let into_future_expr = match await_kind {
@ -1642,14 +1617,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
fn with_try_block_scope<T>(
&mut self,
scope: TryBlockScope,
f: impl FnOnce(&mut Self) -> T,
) -> T {
let old_scope = mem::replace(&mut self.try_block_scope, scope);
fn with_catch_scope<T>(&mut self, catch_id: hir::HirId, f: impl FnOnce(&mut Self) -> T) -> T {
let old_scope = self.catch_scope.replace(catch_id);
let result = f(self);
self.try_block_scope = old_scope;
self.catch_scope = old_scope;
result
}
@ -1702,7 +1673,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
&self.tcx.sess,
sym::yield_expr,
span,
msg!("yield syntax is experimental"),
fluent_generated::ast_lowering_yield,
)
.emit();
}
@ -1817,7 +1788,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let break_expr =
self.with_loop_scope(loop_hir_id, |this| this.expr_break_alloc(for_span));
let pat = self.pat_none(for_span);
self.arm(pat, break_expr, for_span)
self.arm(pat, break_expr)
};
// Some(<pat>) => <body>,
@ -1826,7 +1797,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let body_block =
self.with_loop_scope(loop_hir_id, |this| this.lower_block(body, false));
let body_expr = self.arena.alloc(self.expr_block(body_block));
self.arm(some_pat, body_expr, for_span)
self.arm(some_pat, body_expr)
};
// `mut iter`
@ -1885,7 +1856,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let loop_expr = self.arena.alloc(hir::Expr { hir_id: loop_hir_id, kind, span: for_span });
// `mut iter => { ... }`
let iter_arm = self.arm(iter_pat, loop_expr, for_span);
let iter_arm = self.arm(iter_pat, loop_expr);
let match_expr = match loop_kind {
ForLoopKind::For => {
@ -1930,7 +1901,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::LangItem::IntoAsyncIterIntoIter,
arena_vec![self; head],
);
let iter_arm = self.arm(async_iter_pat, inner_match_expr, for_span);
let iter_arm = self.arm(async_iter_pat, inner_match_expr);
self.arena.alloc(self.expr_match(
for_span,
iter,
@ -1997,7 +1968,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let val_expr = self.expr_ident(span, val_ident, val_pat_nid);
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, try_span)
self.arm(continue_pat, val_expr)
};
// `ControlFlow::Break(residual) =>
@ -2007,25 +1978,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
let residual_ident = Ident::with_dummy_span(sym::residual);
let (residual_local, residual_local_nid) = self.pat_ident(try_span, residual_ident);
let residual_expr = self.expr_ident_mut(try_span, residual_ident, residual_local_nid);
let (constructor_item, target_id) = match self.try_block_scope {
TryBlockScope::Function => {
(hir::LangItem::TryTraitFromResidual, Err(hir::LoopIdError::OutsideLoopScope))
}
TryBlockScope::Homogeneous(block_id) => {
(hir::LangItem::ResidualIntoTryType, Ok(block_id))
}
TryBlockScope::Heterogeneous(block_id) => {
(hir::LangItem::TryTraitFromResidual, Ok(block_id))
}
};
let from_residual_expr = self.wrap_in_try_constructor(
constructor_item,
if self.catch_scope.is_some() {
hir::LangItem::ResidualIntoTryType
} else {
hir::LangItem::TryTraitFromResidual
},
try_span,
self.arena.alloc(residual_expr),
unstable_span,
);
let ret_expr = if target_id.is_ok() {
let ret_expr = if let Some(catch_id) = self.catch_scope {
let target_id = Ok(catch_id);
self.arena.alloc(self.expr(
try_span,
hir::ExprKind::Break(
@ -2040,7 +2004,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
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, try_span)
self.arm(break_pat, ret_expr)
};
hir::ExprKind::Match(
@ -2080,14 +2044,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
yeeted_span,
);
match self.try_block_scope {
TryBlockScope::Homogeneous(block_id) | TryBlockScope::Heterogeneous(block_id) => {
hir::ExprKind::Break(
hir::Destination { label: None, target_id: Ok(block_id) },
Some(from_yeet_expr),
)
}
TryBlockScope::Function => self.checked_return(Some(from_yeet_expr)),
if let Some(catch_id) = self.catch_scope {
let target_id = Ok(catch_id);
hir::ExprKind::Break(hir::Destination { label: None, target_id }, Some(from_yeet_expr))
} else {
self.checked_return(Some(from_yeet_expr))
}
}
@ -2368,13 +2329,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
&mut self,
pat: &'hir hir::Pat<'hir>,
expr: &'hir hir::Expr<'hir>,
span: Span,
) -> hir::Arm<'hir> {
hir::Arm {
hir_id: self.next_id(),
pat,
guard: None,
span: self.lower_span(span),
span: self.lower_span(expr.span),
body: expr,
}
}

View file

@ -281,13 +281,6 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
});
}
fn visit_const_arg_expr_field(&mut self, field: &'hir ConstArgExprField<'hir>) {
self.insert(field.span, field.hir_id, Node::ConstArgExprField(field));
self.with_parent(field.hir_id, |this| {
intravisit::walk_const_arg_expr_field(this, field);
})
}
fn visit_stmt(&mut self, stmt: &'hir Stmt<'hir>) {
self.insert(stmt.span, stmt.hir_id, Node::Stmt(stmt));
@ -312,7 +305,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
fn visit_const_arg(&mut self, const_arg: &'hir ConstArg<'hir, AmbigArg>) {
self.insert(
const_arg.as_unambig_ct().span,
const_arg.as_unambig_ct().span(),
const_arg.hir_id,
Node::ConstArg(const_arg.as_unambig_ct()),
);

View file

@ -2,7 +2,7 @@ use rustc_abi::ExternAbi;
use rustc_ast::visit::AssocCtxt;
use rustc_ast::*;
use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err};
use rustc_hir::attrs::{AttributeKind, EiiImplResolution};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{DefKind, PerNS, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
use rustc_hir::{
@ -11,7 +11,6 @@ use rustc_hir::{
use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::span_bug;
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
use rustc_span::def_id::DefId;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
use smallvec::{SmallVec, smallvec};
@ -134,108 +133,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
fn lower_eii_decl(
&mut self,
id: NodeId,
name: Ident,
EiiDecl { foreign_item, impl_unsafe }: &EiiDecl,
) -> Option<hir::attrs::EiiDecl> {
self.lower_path_simple_eii(id, foreign_item).map(|did| hir::attrs::EiiDecl {
foreign_item: did,
impl_unsafe: *impl_unsafe,
name,
})
}
fn lower_eii_impl(
&mut self,
EiiImpl {
node_id,
eii_macro_path,
impl_safety,
span,
inner_span,
is_default,
known_eii_macro_resolution,
}: &EiiImpl,
) -> hir::attrs::EiiImpl {
let resolution = if let Some(target) = known_eii_macro_resolution
&& let Some(decl) = self.lower_eii_decl(
*node_id,
// the expect is ok here since we always generate this path in the eii macro.
eii_macro_path.segments.last().expect("at least one segment").ident,
target,
) {
EiiImplResolution::Known(decl)
} else if let Some(macro_did) = self.lower_path_simple_eii(*node_id, eii_macro_path) {
EiiImplResolution::Macro(macro_did)
} else {
EiiImplResolution::Error(
self.dcx().span_delayed_bug(*span, "eii never resolved without errors given"),
)
};
hir::attrs::EiiImpl {
span: self.lower_span(*span),
inner_span: self.lower_span(*inner_span),
impl_marked_unsafe: self.lower_safety(*impl_safety, hir::Safety::Safe).is_unsafe(),
is_default: *is_default,
resolution,
}
}
fn generate_extra_attrs_for_item_kind(
&mut self,
id: NodeId,
i: &ItemKind,
) -> Vec<hir::Attribute> {
match i {
ItemKind::Fn(box Fn { eii_impls, .. }) if eii_impls.is_empty() => Vec::new(),
ItemKind::Fn(box Fn { eii_impls, .. }) => {
vec![hir::Attribute::Parsed(AttributeKind::EiiImpls(
eii_impls.iter().map(|i| self.lower_eii_impl(i)).collect(),
))]
}
ItemKind::MacroDef(name, MacroDef { eii_declaration: Some(target), .. }) => self
.lower_eii_decl(id, *name, target)
.map(|decl| vec![hir::Attribute::Parsed(AttributeKind::EiiDeclaration(decl))])
.unwrap_or_default(),
ItemKind::ExternCrate(..)
| ItemKind::Use(..)
| ItemKind::Static(..)
| ItemKind::Const(..)
| ItemKind::ConstBlock(..)
| ItemKind::Mod(..)
| ItemKind::ForeignMod(..)
| ItemKind::GlobalAsm(..)
| ItemKind::TyAlias(..)
| ItemKind::Enum(..)
| ItemKind::Struct(..)
| ItemKind::Union(..)
| ItemKind::Trait(..)
| ItemKind::TraitAlias(..)
| ItemKind::Impl(..)
| ItemKind::MacCall(..)
| ItemKind::MacroDef(..)
| ItemKind::Delegation(..)
| ItemKind::DelegationMac(..) => Vec::new(),
}
}
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 extra_hir_attributes = self.generate_extra_attrs_for_item_kind(i.id, &i.kind);
let attrs = self.lower_attrs_with_extra(
hir_id,
&i.attrs,
i.span,
Target::from_ast_item(i),
&extra_hir_attributes,
);
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(),
@ -243,7 +144,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
vis_span,
span: self.lower_span(i.span),
has_delayed_lints: !self.delayed_lints.is_empty(),
eii: find_attr!(attrs, AttributeKind::EiiImpls(..) | AttributeKind::EiiDeclaration(..)),
};
self.arena.alloc(item)
}
@ -277,19 +177,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
define_opaque,
}) => {
let ident = self.lower_ident(*ident);
let ty = self
.lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy));
let ty =
self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy));
let body_id = self.lower_const_body(span, e.as_deref());
self.lower_define_opaque(hir_id, define_opaque);
hir::ItemKind::Static(*m, ident, ty, body_id)
}
ItemKind::Const(box ConstItem {
defaultness: _,
ident,
generics,
ty,
rhs_kind,
define_opaque,
ItemKind::Const(box ast::ConstItem {
ident, generics, ty, rhs, define_opaque, ..
}) => {
let ident = self.lower_ident(*ident);
let (generics, (ty, rhs)) = self.lower_generics(
@ -297,30 +192,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
let ty = this.lower_ty_alloc(
ty,
ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy),
);
let rhs = this.lower_const_item_rhs(rhs_kind, span);
let ty = this
.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
let rhs = this.lower_const_item_rhs(attrs, rhs.as_ref(), span);
(ty, rhs)
},
);
self.lower_define_opaque(hir_id, &define_opaque);
hir::ItemKind::Const(ident, generics, ty, rhs)
}
ItemKind::ConstBlock(ConstBlockItem { span, id, block }) => hir::ItemKind::Const(
self.lower_ident(ConstBlockItem::IDENT),
hir::Generics::empty(),
self.arena.alloc(self.ty_tup(DUMMY_SP, &[])),
hir::ConstItemRhs::Body({
let body = hir::Expr {
hir_id: self.lower_node_id(*id),
kind: hir::ExprKind::Block(self.lower_block(block, false), None),
span: self.lower_span(*span),
};
self.record_body(&[], body)
}),
),
ItemKind::Fn(box Fn {
sig: FnSig { decl, header, span: fn_sig_span },
ident,
@ -412,7 +292,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
this.arena.alloc(this.ty(span, hir::TyKind::Err(guar)))
}
Some(ty) => this.lower_ty_alloc(
Some(ty) => this.lower_ty(
ty,
ImplTraitContext::OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias {
@ -486,7 +366,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
.as_deref()
.map(|of_trait| this.lower_trait_impl_header(of_trait));
let lowered_ty = this.lower_ty_alloc(
let lowered_ty = this.lower_ty(
ty,
ImplTraitContext::Disallowed(ImplTraitPosition::ImplSelf),
);
@ -555,7 +435,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
hir::ItemKind::TraitAlias(constness, ident, generics, bounds)
}
ItemKind::MacroDef(ident, MacroDef { body, macro_rules, eii_declaration: _ }) => {
ItemKind::MacroDef(ident, MacroDef { body, macro_rules }) => {
let ident = self.lower_ident(*ident);
let body = Box::new(self.lower_delim_args(body));
let def_id = self.local_def_id(id);
@ -566,15 +446,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
def_kind.descr(def_id.to_def_id())
);
};
let macro_def = self.arena.alloc(ast::MacroDef {
body,
macro_rules: *macro_rules,
eii_declaration: None,
});
let macro_def = self.arena.alloc(ast::MacroDef { body, macro_rules: *macro_rules });
hir::ItemKind::Macro(ident, macro_def, macro_kinds)
}
ItemKind::Delegation(box delegation) => {
let delegation_results = self.lower_delegation(delegation, id);
let delegation_results = self.lower_delegation(delegation, id, false);
hir::ItemKind::Fn {
sig: delegation_results.sig,
ident: delegation_results.ident,
@ -589,16 +465,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
fn lower_path_simple_eii(&mut self, id: NodeId, path: &Path) -> Option<DefId> {
let res = self.resolver.get_partial_res(id)?;
let Some(did) = res.expect_full_res().opt_def_id() else {
self.dcx().span_delayed_bug(path.span, "should have errored in resolve");
return None;
};
Some(did)
}
#[instrument(level = "debug", skip(self))]
fn lower_use_tree(
&mut self,
@ -707,10 +573,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
vis_span,
span: this.lower_span(use_tree.span),
has_delayed_lints: !this.delayed_lints.is_empty(),
eii: find_attr!(
attrs,
AttributeKind::EiiImpls(..) | AttributeKind::EiiDeclaration(..)
),
};
hir::OwnerNode::Item(this.arena.alloc(item))
});
@ -791,8 +653,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
safety,
define_opaque,
}) => {
let ty = self
.lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy));
let ty =
self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy));
let safety = self.lower_safety(*safety, hir::Safety::Unsafe);
if define_opaque.is_some() {
self.dcx().span_err(i.span, "foreign statics cannot define opaque types");
@ -827,10 +689,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir_id,
def_id: self.local_def_id(v.id),
data: self.lower_variant_data(hir_id, item_kind, &v.data),
disr_expr: v
.disr_expr
.as_ref()
.map(|e| self.lower_anon_const_to_anon_const(e, e.value.span)),
disr_expr: v.disr_expr.as_ref().map(|e| self.lower_anon_const_to_anon_const(e)),
ident: self.lower_ident(v.ident),
span: self.lower_span(v.span),
}
@ -906,8 +765,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
&mut self,
(index, f): (usize, &FieldDef),
) -> hir::FieldDef<'hir> {
let ty =
self.lower_ty_alloc(&f.ty, ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy));
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, Target::Field);
hir::FieldDef {
@ -920,10 +778,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
None => Ident::new(sym::integer(index), self.lower_span(f.span)),
},
vis_span: self.lower_span(f.vis.span),
default: f
.default
.as_ref()
.map(|v| self.lower_anon_const_to_anon_const(v, v.value.span)),
default: f.default.as_ref().map(|v| self.lower_anon_const_to_anon_const(v)),
ty,
safety: self.lower_safety(f.safety, hir::Safety::Safe),
}
@ -939,36 +794,26 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
let trait_item_def_id = hir_id.expect_owner();
let (ident, generics, kind, has_value) = match &i.kind {
let (ident, generics, kind, has_default) = match &i.kind {
AssocItemKind::Const(box ConstItem {
ident,
generics,
ty,
rhs_kind,
define_opaque,
..
ident, generics, ty, rhs, define_opaque, ..
}) => {
let (generics, kind) = self.lower_generics(
generics,
i.id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
let ty = this.lower_ty_alloc(
ty,
ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy),
);
// Trait associated consts don't need an expression/body.
let rhs = if rhs_kind.has_expr() {
Some(this.lower_const_item_rhs(rhs_kind, i.span))
} else {
None
};
hir::TraitItemKind::Const(ty, rhs, rhs_kind.is_type_const().into())
let ty = this
.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
let rhs = rhs
.as_ref()
.map(|rhs| this.lower_const_item_rhs(attrs, Some(rhs), i.span));
hir::TraitItemKind::Const(ty, rhs)
},
);
if define_opaque.is_some() {
if rhs_kind.has_expr() {
if rhs.is_some() {
self.lower_define_opaque(hir_id, &define_opaque);
} else {
self.dcx().span_err(
@ -978,7 +823,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
(*ident, generics, kind, rhs_kind.has_expr())
(*ident, generics, kind, rhs.is_some())
}
AssocItemKind::Fn(box Fn {
sig, ident, generics, body: None, define_opaque, ..
@ -1058,7 +903,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
let ty = ty.as_ref().map(|x| {
this.lower_ty_alloc(
this.lower_ty(
x,
ImplTraitContext::Disallowed(ImplTraitPosition::AssocTy),
)
@ -1076,7 +921,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
(*ident, generics, kind, ty.is_some())
}
AssocItemKind::Delegation(box delegation) => {
let delegation_results = self.lower_delegation(delegation, i.id);
let delegation_results = self.lower_delegation(delegation, i.id, false);
let item_kind = hir::TraitItemKind::Fn(
delegation_results.sig,
hir::TraitFn::Provided(delegation_results.body_id),
@ -1088,17 +933,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
};
let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value, || {
hir::Defaultness::Default { has_value }
});
let item = hir::TraitItem {
owner_id: trait_item_def_id,
ident: self.lower_ident(ident),
generics,
kind,
span: self.lower_span(i.span),
defaultness,
defaultness: hir::Defaultness::Default { has_value: has_default },
has_delayed_lints: !self.delayed_lints.is_empty(),
};
self.arena.alloc(item)
@ -1126,8 +967,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// `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, || hir::Defaultness::Final);
let (defaultness, defaultness_span) = self.lower_defaultness(defaultness, has_val);
let modifiers = TraitBoundModifiers {
constness: BoundConstness::Never,
asyncness: BoundAsyncness::Normal,
@ -1156,8 +996,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
) -> &'hir hir::ImplItem<'hir> {
// Since `default impl` is not yet implemented, this is always true in impls.
let has_value = true;
let (defaultness, _) =
self.lower_defaultness(i.kind.defaultness(), has_value, || hir::Defaultness::Final);
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,
@ -1168,12 +1007,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let (ident, (generics, kind)) = match &i.kind {
AssocItemKind::Const(box ConstItem {
ident,
generics,
ty,
rhs_kind,
define_opaque,
..
ident, generics, ty, rhs, define_opaque, ..
}) => (
*ident,
self.lower_generics(
@ -1181,12 +1015,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
i.id,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
let ty = this.lower_ty_alloc(
ty,
ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy),
);
let ty = this
.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
this.lower_define_opaque(hir_id, &define_opaque);
let rhs = this.lower_const_item_rhs(rhs_kind, i.span);
let rhs = this.lower_const_item_rhs(attrs, rhs.as_ref(), i.span);
hir::ImplItemKind::Const(ty, rhs)
},
),
@ -1243,7 +1075,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ImplItemKind::Type(ty)
}
Some(ty) => {
let ty = this.lower_ty_alloc(
let ty = this.lower_ty(
ty,
ImplTraitContext::OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias {
@ -1259,7 +1091,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
}
AssocItemKind::Delegation(box delegation) => {
let delegation_results = self.lower_delegation(delegation, i.id);
let delegation_results = self.lower_delegation(delegation, i.id, is_in_trait_impl);
(
delegation.ident,
(
@ -1310,14 +1142,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
&self,
d: Defaultness,
has_value: bool,
implicit: impl FnOnce() -> hir::Defaultness,
) -> (hir::Defaultness, Option<Span>) {
match d {
Defaultness::Implicit => (implicit(), None),
Defaultness::Default(sp) => {
(hir::Defaultness::Default { has_value }, Some(self.lower_span(sp)))
}
Defaultness::Final(sp) => (hir::Defaultness::Final, Some(self.lower_span(sp))),
Defaultness::Final => {
assert!(has_value);
(hir::Defaultness::Final, None)
}
}
}
@ -1415,7 +1248,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// create a fake body so that the entire rest of the compiler doesn't have to deal with
// this as a special case.
return self.lower_fn_body(decl, contract, |this| {
if find_attr!(attrs, AttributeKind::RustcIntrinsic)
if attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic))
|| this.tcx.is_sdylib_interface_build()
{
let span = this.lower_span(span);
@ -1978,7 +1811,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
bound_generic_params,
hir::GenericParamSource::Binder,
),
bounded_ty: self.lower_ty_alloc(
bounded_ty: self.lower_ty(
bounded_ty,
ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
),
@ -2007,14 +1840,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
WherePredicateKind::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty }) => {
hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate {
lhs_ty: self.lower_ty_alloc(
lhs_ty,
ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
),
rhs_ty: self.lower_ty_alloc(
rhs_ty,
ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
),
lhs_ty: self
.lower_ty(lhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Bound)),
rhs_ty: self
.lower_ty(rhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Bound)),
})
}
});

View file

@ -35,7 +35,6 @@
#![feature(if_let_guard)]
// tidy-alphabetical-end
use std::mem;
use std::sync::Arc;
use rustc_ast::node_id::NodeMap;
@ -47,14 +46,13 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::spawn;
use rustc_data_structures::tagged_ptr::TaggedRef;
use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle};
use rustc_hir::attrs::AttributeKind;
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::{AttributeLint, DelayedLint};
use rustc_hir::lints::DelayedLint;
use rustc_hir::{
self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LifetimeSource,
LifetimeSyntax, ParamName, Target, TraitCandidate, find_attr,
LifetimeSyntax, ParamName, Target, TraitCandidate,
};
use rustc_index::{Idx, IndexSlice, IndexVec};
use rustc_macros::extension;
@ -88,6 +86,8 @@ mod pat;
mod path;
pub mod stability;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
struct LoweringContext<'a, 'hir> {
tcx: TyCtxt<'hir>,
resolver: &'a mut ResolverAstLowering,
@ -117,7 +117,7 @@ struct LoweringContext<'a, 'hir> {
/// outside of an `async fn`.
current_item: Option<Span>,
try_block_scope: TryBlockScope,
catch_scope: Option<HirId>,
loop_scope: Option<HirId>,
is_in_loop_condition: bool,
is_in_dyn_type: bool,
@ -173,7 +173,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
trait_map: Default::default(),
// Lowering state.
try_block_scope: TryBlockScope::Function,
catch_scope: None,
loop_scope: None,
is_in_loop_condition: false,
is_in_dyn_type: false,
@ -235,19 +235,15 @@ impl SpanLowerer {
#[extension(trait ResolverAstLoweringExt)]
impl ResolverAstLowering {
fn legacy_const_generic_args(&self, expr: &Expr, tcx: TyCtxt<'_>) -> Option<Vec<usize>> {
let ExprKind::Path(None, path) = &expr.kind else {
return None;
};
fn legacy_const_generic_args(&self, expr: &Expr) -> Option<Vec<usize>> {
if let ExprKind::Path(None, path) = &expr.kind {
// Don't perform legacy const generics rewriting if the path already
// has generic arguments.
if path.segments.last().unwrap().args.is_some() {
return None;
}
let def_id = self.partial_res_map.get(&expr.id)?.full_res()?.opt_def_id()?;
if let Res::Def(DefKind::Fn, def_id) = self.partial_res_map.get(&expr.id)?.full_res()? {
// We only support cross-crate argument rewriting. Uses
// within the same crate should be updated to use the new
// const generics style.
@ -255,12 +251,13 @@ impl ResolverAstLowering {
return None;
}
find_attr!(
// we can use parsed attrs here since for other crates they're already available
tcx.get_all_attrs(def_id),
AttributeKind::RustcLegacyConstGenerics{fn_indexes,..} => fn_indexes
)
.map(|fn_indexes| fn_indexes.iter().map(|(num, _)| *num).collect())
if let Some(v) = self.legacy_const_generic_args.get(&def_id) {
return v.clone();
}
}
}
None
}
fn get_partial_res(&self, id: NodeId) -> Option<PartialRes> {
@ -419,18 +416,6 @@ enum AstOwner<'a> {
ForeignItem(&'a ast::ForeignItem),
}
#[derive(Copy, Clone, Debug)]
enum TryBlockScope {
/// There isn't a `try` block, so a `?` will use `return`.
Function,
/// We're inside a `try { … }` block, so a `?` will block-break
/// from that block using a type depending only on the argument.
Homogeneous(HirId),
/// We're inside a `try as _ { … }` block, so a `?` will block-break
/// from that block using the type specified.
Heterogeneous(HirId),
}
fn index_crate<'a>(
node_id_to_def_id: &NodeMap<LocalDefId>,
krate: &'a Crate,
@ -878,7 +863,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
(hir::ParamName::Fresh, hir::LifetimeParamKind::Elided(kind))
}
LifetimeRes::Static { .. } | LifetimeRes::Error(..) => return None,
LifetimeRes::Static { .. } | LifetimeRes::Error => return None,
res => panic!(
"Unexpected lifetime resolution {:?} for {:?} at {:?}",
res, ident, ident.span
@ -951,10 +936,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let old_contract = self.contract_ensures.take();
let try_block_scope = mem::replace(&mut self.try_block_scope, TryBlockScope::Function);
let catch_scope = self.catch_scope.take();
let loop_scope = self.loop_scope.take();
let ret = f(self);
self.try_block_scope = try_block_scope;
self.catch_scope = catch_scope;
self.loop_scope = loop_scope;
self.contract_ensures = old_contract;
@ -973,23 +958,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
target_span: Span,
target: Target,
) -> &'hir [hir::Attribute] {
self.lower_attrs_with_extra(id, attrs, target_span, target, &[])
}
fn lower_attrs_with_extra(
&mut self,
id: HirId,
attrs: &[Attribute],
target_span: Span,
target: Target,
extra_hir_attributes: &[hir::Attribute],
) -> &'hir [hir::Attribute] {
if attrs.is_empty() && extra_hir_attributes.is_empty() {
if attrs.is_empty() {
&[]
} else {
let mut lowered_attrs =
let lowered_attrs =
self.lower_attrs_vec(attrs, self.lower_span(target_span), id, target);
lowered_attrs.extend(extra_hir_attributes.iter().cloned());
assert_eq!(id.owner, self.current_hir_id_owner);
let ret = self.arena.alloc_from_iter(lowered_attrs);
@ -1020,16 +993,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.attribute_parser.parse_attribute_list(
attrs,
target_span,
target_hir_id,
target,
OmitDoc::Lower,
|s| l.lower(s),
|lint_id, span, kind| {
self.delayed_lints.push(DelayedLint::AttributeParsing(AttributeLint {
lint_id,
id: target_hir_id,
span,
kind,
}));
|l| {
self.delayed_lints.push(DelayedLint::AttributeParsing(l));
},
)
}
@ -1127,8 +1096,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let kind = match &constraint.kind {
AssocItemConstraintKind::Equality { term } => {
let term = match term {
Term::Ty(ty) => self.lower_ty_alloc(ty, itctx).into(),
Term::Const(c) => self.lower_anon_const_to_const_arg_and_alloc(c).into(),
Term::Ty(ty) => self.lower_ty(ty, itctx).into(),
Term::Const(c) => self.lower_anon_const_to_const_arg(c).into(),
};
hir::AssocItemConstraintKind::Equality { term }
}
@ -1237,7 +1206,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.and_then(|partial_res| partial_res.full_res())
{
if !res.matches_ns(Namespace::TypeNS)
&& path.is_potential_trivial_const_arg()
&& path.is_potential_trivial_const_arg(false)
{
debug!(
"lower_generic_arg: Lowering type argument as const argument: {:?}",
@ -1252,17 +1221,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
_ => {}
}
GenericArg::Type(self.lower_ty_alloc(ty, itctx).try_as_ambig_ty().unwrap())
GenericArg::Type(self.lower_ty(ty, itctx).try_as_ambig_ty().unwrap())
}
ast::GenericArg::Const(ct) => {
GenericArg::Const(self.lower_anon_const_to_const_arg(ct).try_as_ambig_ct().unwrap())
}
ast::GenericArg::Const(ct) => GenericArg::Const(
self.lower_anon_const_to_const_arg_and_alloc(ct).try_as_ambig_ct().unwrap(),
),
}
}
#[instrument(level = "debug", skip(self))]
fn lower_ty_alloc(&mut self, t: &Ty, itctx: ImplTraitContext) -> &'hir hir::Ty<'hir> {
self.arena.alloc(self.lower_ty(t, itctx))
fn lower_ty(&mut self, t: &Ty, itctx: ImplTraitContext) -> &'hir hir::Ty<'hir> {
self.arena.alloc(self.lower_ty_direct(t, itctx))
}
fn lower_path_ty(
@ -1326,11 +1295,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.ty(span, hir::TyKind::Tup(tys))
}
fn lower_ty(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir> {
fn lower_ty_direct(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir> {
let kind = match &t.kind {
TyKind::Infer => hir::TyKind::Infer(()),
TyKind::Err(guar) => hir::TyKind::Err(*guar),
TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty_alloc(ty, itctx)),
TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
TyKind::Ref(region, mt) => {
let lifetime = self.lower_ty_direct_lifetime(t, *region);
@ -1364,15 +1333,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params);
hir::TyKind::UnsafeBinder(self.arena.alloc(hir::UnsafeBinderTy {
generic_params,
inner_ty: self.lower_ty_alloc(&f.inner_ty, itctx),
inner_ty: self.lower_ty(&f.inner_ty, itctx),
}))
}
TyKind::Never => hir::TyKind::Never,
TyKind::Tup(tys) => hir::TyKind::Tup(
self.arena.alloc_from_iter(tys.iter().map(|ty| self.lower_ty(ty, itctx))),
self.arena.alloc_from_iter(tys.iter().map(|ty| self.lower_ty_direct(ty, itctx))),
),
TyKind::Paren(ty) => {
return self.lower_ty(ty, itctx);
return self.lower_ty_direct(ty, itctx);
}
TyKind::Path(qself, path) => {
return self.lower_path_ty(t, qself, path, ParamMode::Explicit, itctx);
@ -1395,7 +1364,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
))
}
TyKind::Array(ty, length) => hir::TyKind::Array(
self.lower_ty_alloc(ty, itctx),
self.lower_ty(ty, itctx),
self.lower_array_length_to_const_arg(length),
),
TyKind::TraitObject(bounds, kind) => {
@ -1495,7 +1464,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
TyKind::Pat(ty, pat) => {
hir::TyKind::Pat(self.lower_ty_alloc(ty, itctx), self.lower_ty_pat(pat, ty.span))
hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat, ty.span))
}
TyKind::MacCall(_) => {
span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now")
@ -1695,13 +1664,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
ImplTraitContext::Disallowed(ImplTraitPosition::PointerParam)
}
};
self.lower_ty(&param.ty, itctx)
self.lower_ty_direct(&param.ty, itctx)
}));
let output = match coro {
Some(coro) => {
let fn_def_id = self.local_def_id(fn_node_id);
self.lower_coroutine_fn_ret_ty(&decl.output, fn_def_id, coro, kind)
self.lower_coroutine_fn_ret_ty(&decl.output, fn_def_id, coro, kind, fn_span)
}
None => match &decl.output {
FnRetTy::Ty(ty) => {
@ -1734,7 +1703,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
ImplTraitContext::Disallowed(ImplTraitPosition::PointerReturn)
}
};
hir::FnRetTy::Return(self.lower_ty_alloc(ty, itctx))
hir::FnRetTy::Return(self.lower_ty(ty, itctx))
}
FnRetTy::Default(span) => hir::FnRetTy::DefaultReturn(self.lower_span(*span)),
},
@ -1786,8 +1755,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn_def_id: LocalDefId,
coro: CoroutineKind,
fn_kind: FnDeclKind,
fn_span: Span,
) -> hir::FnRetTy<'hir> {
let span = self.lower_span(output.span());
let span = self.lower_span(fn_span);
let (opaque_ty_node_id, allowed_features) = match coro {
CoroutineKind::Async { return_impl_trait_id, .. } => (return_impl_trait_id, None),
@ -1845,7 +1815,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// Not `OpaqueTyOrigin::AsyncFn`: that's only used for the
// `impl Future` opaque type that `async fn` implicitly
// generates.
self.lower_ty_alloc(ty, itctx)
self.lower_ty(ty, itctx)
}
FnRetTy::Default(ret_ty_span) => self.arena.alloc(self.ty_tup(*ret_ty_span, &[])),
};
@ -1931,8 +1901,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
source: LifetimeSource,
syntax: LifetimeSyntax,
) -> &'hir hir::Lifetime {
let res = if let Some(res) = self.resolver.get_lifetime_res(id) {
match res {
let res = self.resolver.get_lifetime_res(id).unwrap_or(LifetimeRes::Error);
let res = match res {
LifetimeRes::Param { param, .. } => hir::LifetimeKind::Param(param),
LifetimeRes::Fresh { param, .. } => {
assert_eq!(ident.name, kw::UnderscoreLifetime);
@ -1947,13 +1917,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
assert!(matches!(ident.name, kw::StaticLifetime | kw::UnderscoreLifetime));
hir::LifetimeKind::Static
}
LifetimeRes::Error(guar) => hir::LifetimeKind::Error(guar),
LifetimeRes::Error => hir::LifetimeKind::Error,
LifetimeRes::ElidedAnchor { .. } => {
panic!("Unexpected `ElidedAnchar` {:?} at {:?}", ident, ident.span);
}
}
} else {
hir::LifetimeKind::Error(self.dcx().span_delayed_bug(ident.span, "unresolved lifetime"))
};
debug!(?res);
@ -1991,9 +1958,8 @@ 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);
let param_attrs = &param.attrs;
let param_span = param.span();
let param = hir::GenericParam {
self.lower_attrs(hir_id, &param.attrs, param.span(), Target::Param);
hir::GenericParam {
hir_id,
def_id: self.local_def_id(param.id),
name,
@ -2002,9 +1968,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
kind,
colon_span: param.colon_span.map(|s| self.lower_span(s)),
source,
};
self.lower_attrs(hir_id, param_attrs, param_span, Target::from_generic_param(&param));
param
}
}
fn lower_generic_param_kind(
@ -2017,9 +1981,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// AST resolution emitted an error on those parameters, so we lower them using
// `ParamName::Error`.
let ident = self.lower_ident(param.ident);
let param_name = if let Some(LifetimeRes::Error(..)) =
self.resolver.get_lifetime_res(param.id)
{
let param_name =
if let Some(LifetimeRes::Error) = self.resolver.get_lifetime_res(param.id) {
ParamName::Error(ident)
} else {
ParamName::Plain(ident)
@ -2045,7 +2008,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
})
.map(|def| {
self.lower_ty_alloc(
self.lower_ty(
def,
ImplTraitContext::Disallowed(ImplTraitPosition::GenericDefault),
)
@ -2056,10 +2019,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
(hir::ParamName::Plain(self.lower_ident(param.ident)), kind)
}
GenericParamKind::Const { ty, span: _, default } => {
let ty = self.lower_ty_alloc(
ty,
ImplTraitContext::Disallowed(ImplTraitPosition::GenericDefault),
);
let ty = self
.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::GenericDefault));
// Not only do we deny const param defaults in binders but we also map them to `None`
// since later compiler stages cannot handle them (and shouldn't need to be able to).
@ -2075,7 +2036,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
false
}
})
.map(|def| self.lower_anon_const_to_const_arg_and_alloc(def));
.map(|def| self.lower_anon_const_to_const_arg(def));
(
hir::ParamName::Plain(self.lower_ident(param.ident)),
@ -2209,7 +2170,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
fn lower_mt(&mut self, mt: &MutTy, itctx: ImplTraitContext) -> hir::MutTy<'hir> {
hir::MutTy { ty: self.lower_ty_alloc(&mt.ty, itctx), mutbl: mt.mutbl }
hir::MutTy { ty: self.lower_ty(&mt.ty, itctx), mutbl: mt.mutbl }
}
#[instrument(level = "debug", skip(self), ret)]
@ -2294,14 +2255,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// `ExprKind::Paren(ExprKind::Underscore)` and should also be lowered to `GenericArg::Infer`
match c.value.peel_parens().kind {
ExprKind::Underscore => {
let ct_kind = hir::ConstArgKind::Infer(());
self.arena.alloc(hir::ConstArg {
hir_id: self.lower_node_id(c.id),
kind: ct_kind,
span: self.lower_span(c.value.span),
})
let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span), ());
self.arena.alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind })
}
_ => self.lower_anon_const_to_const_arg_and_alloc(c),
_ => self.lower_anon_const_to_const_arg(c),
}
}
@ -2318,9 +2275,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
) -> &'hir hir::ConstArg<'hir> {
let tcx = self.tcx;
let is_trivial_path = path.is_potential_trivial_const_arg()
&& matches!(res, Res::Def(DefKind::ConstParam, _));
let ct_kind = if is_trivial_path || tcx.features().min_generic_const_args() {
let ct_kind = if path
.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
&& (tcx.features().min_generic_const_args()
|| matches!(res, Res::Def(DefKind::ConstParam, _)))
{
let qpath = self.lower_qpath(
ty_id,
&None,
@ -2369,242 +2328,45 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir::ConstArgKind::Anon(ct)
};
self.arena.alloc(hir::ConstArg {
hir_id: self.next_id(),
kind: ct_kind,
span: self.lower_span(span),
})
self.arena.alloc(hir::ConstArg { hir_id: self.next_id(), kind: ct_kind })
}
fn lower_const_item_rhs(
&mut self,
rhs_kind: &ConstItemRhsKind,
attrs: &[hir::Attribute],
rhs: Option<&ConstItemRhs>,
span: Span,
) -> hir::ConstItemRhs<'hir> {
match rhs_kind {
ConstItemRhsKind::Body { rhs: Some(body) } => {
hir::ConstItemRhs::Body(self.lower_const_body(span, Some(body)))
match rhs {
Some(ConstItemRhs::TypeConst(anon)) => {
hir::ConstItemRhs::TypeConst(self.lower_anon_const_to_const_arg(anon))
}
ConstItemRhsKind::Body { rhs: None } => {
hir::ConstItemRhs::Body(self.lower_const_body(span, None))
}
ConstItemRhsKind::TypeConst { rhs: Some(anon) } => {
hir::ConstItemRhs::TypeConst(self.lower_anon_const_to_const_arg_and_alloc(anon))
}
ConstItemRhsKind::TypeConst { rhs: None } => {
None if attr::contains_name(attrs, sym::type_const) => {
let const_arg = ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::Error(
DUMMY_SP,
self.dcx().span_delayed_bug(DUMMY_SP, "no block"),
),
span: DUMMY_SP,
};
hir::ConstItemRhs::TypeConst(self.arena.alloc(const_arg))
}
Some(ConstItemRhs::Body(body)) => {
hir::ConstItemRhs::Body(self.lower_const_body(span, Some(body)))
}
}
#[instrument(level = "debug", skip(self), ret)]
fn lower_expr_to_const_arg_direct(&mut self, expr: &Expr) -> hir::ConstArg<'hir> {
let span = self.lower_span(expr.span);
let overly_complex_const = |this: &mut Self| {
let e = this.dcx().struct_span_err(
expr.span,
"complex const arguments must be placed inside of a `const` block",
);
ConstArg { hir_id: this.next_id(), kind: hir::ConstArgKind::Error(e.emit()), span }
};
match &expr.kind {
ExprKind::Call(func, args) if let ExprKind::Path(qself, path) = &func.kind => {
let qpath = self.lower_qpath(
func.id,
qself,
path,
ParamMode::Explicit,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
let lowered_args = self.arena.alloc_from_iter(args.iter().map(|arg| {
let const_arg = self.lower_expr_to_const_arg_direct(arg);
&*self.arena.alloc(const_arg)
}));
ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::TupleCall(qpath, lowered_args),
span,
}
}
ExprKind::Tup(exprs) => {
let exprs = self.arena.alloc_from_iter(exprs.iter().map(|expr| {
let expr = self.lower_expr_to_const_arg_direct(&expr);
&*self.arena.alloc(expr)
}));
ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Tup(exprs), span }
}
ExprKind::Path(qself, path) => {
let qpath = self.lower_qpath(
expr.id,
qself,
path,
ParamMode::Explicit,
AllowReturnTypeNotation::No,
// FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Path(qpath), span }
}
ExprKind::Struct(se) => {
let path = self.lower_qpath(
expr.id,
&se.qself,
&se.path,
// FIXME(mgca): we may want this to be `Optional` instead, but
// we would also need to make sure that HIR ty lowering errors
// when these paths wind up in signatures.
ParamMode::Explicit,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
let fields = self.arena.alloc_from_iter(se.fields.iter().map(|f| {
let hir_id = self.lower_node_id(f.id);
// FIXME(mgca): This might result in lowering attributes that
// then go unused as the `Target::ExprField` is not actually
// corresponding to `Node::ExprField`.
self.lower_attrs(hir_id, &f.attrs, f.span, Target::ExprField);
let expr = self.lower_expr_to_const_arg_direct(&f.expr);
&*self.arena.alloc(hir::ConstArgExprField {
hir_id,
field: self.lower_ident(f.ident),
expr: self.arena.alloc(expr),
span: self.lower_span(f.span),
})
}));
ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::Struct(path, fields),
span,
}
}
ExprKind::Array(elements) => {
let lowered_elems = self.arena.alloc_from_iter(elements.iter().map(|element| {
let const_arg = self.lower_expr_to_const_arg_direct(element);
&*self.arena.alloc(const_arg)
}));
let array_expr = self.arena.alloc(hir::ConstArgArrayExpr {
span: self.lower_span(expr.span),
elems: lowered_elems,
});
ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::Array(array_expr),
span,
}
}
ExprKind::Underscore => ConstArg {
hir_id: self.lower_node_id(expr.id),
kind: hir::ConstArgKind::Infer(()),
span,
},
ExprKind::Block(block, _) => {
if let [stmt] = block.stmts.as_slice()
&& let StmtKind::Expr(expr) = &stmt.kind
&& matches!(
expr.kind,
ExprKind::Block(..)
| ExprKind::Path(..)
| ExprKind::Struct(..)
| ExprKind::Call(..)
| ExprKind::Tup(..)
| ExprKind::Array(..)
| ExprKind::ConstBlock(..)
)
{
return self.lower_expr_to_const_arg_direct(expr);
}
overly_complex_const(self)
}
ExprKind::Lit(literal) => {
let span = expr.span;
let literal = self.lower_lit(literal, span);
ConstArg {
hir_id: self.lower_node_id(expr.id),
kind: hir::ConstArgKind::Literal { lit: literal.node, negated: false },
span,
}
}
ExprKind::Unary(UnOp::Neg, inner_expr)
if let ExprKind::Lit(literal) = &inner_expr.kind =>
{
let span = expr.span;
let literal = self.lower_lit(literal, span);
ConstArg {
hir_id: self.lower_node_id(expr.id),
kind: hir::ConstArgKind::Literal { lit: literal.node, negated: true },
span,
}
}
ExprKind::ConstBlock(anon_const) => {
let def_id = self.local_def_id(anon_const.id);
assert_eq!(DefKind::AnonConst, self.tcx.def_kind(def_id));
self.lower_anon_const_to_const_arg(anon_const, span)
}
_ => overly_complex_const(self),
None => hir::ConstItemRhs::Body(self.lower_const_body(span, None)),
}
}
/// See [`hir::ConstArg`] for when to use this function vs
/// [`Self::lower_anon_const_to_anon_const`].
fn lower_anon_const_to_const_arg_and_alloc(
&mut self,
anon: &AnonConst,
) -> &'hir hir::ConstArg<'hir> {
self.arena.alloc(self.lower_anon_const_to_const_arg(anon, anon.value.span))
fn lower_anon_const_to_const_arg(&mut self, anon: &AnonConst) -> &'hir hir::ConstArg<'hir> {
self.arena.alloc(self.lower_anon_const_to_const_arg_direct(anon))
}
#[instrument(level = "debug", skip(self))]
fn lower_anon_const_to_const_arg(
&mut self,
anon: &AnonConst,
span: Span,
) -> hir::ConstArg<'hir> {
fn lower_anon_const_to_const_arg_direct(&mut self, anon: &AnonConst) -> hir::ConstArg<'hir> {
let tcx = self.tcx;
// We cannot change parsing depending on feature gates available,
// we can only require feature gates to be active as a delayed check.
// Thus we just parse anon consts generally and make the real decision
// making in ast lowering.
// FIXME(min_generic_const_args): revisit once stable
if tcx.features().min_generic_const_args() {
return match anon.mgca_disambiguation {
MgcaDisambiguation::AnonConst => {
let lowered_anon = self.lower_anon_const_to_anon_const(anon, span);
ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::Anon(lowered_anon),
span: lowered_anon.span,
}
}
MgcaDisambiguation::Direct => self.lower_expr_to_const_arg_direct(&anon.value),
};
}
// Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
// currently have to be wrapped in curly brackets, so it's necessary to special-case.
let expr = if let ExprKind::Block(block, _) = &anon.value.kind
@ -2616,12 +2378,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} else {
&anon.value
};
let maybe_res =
self.resolver.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res());
if let ExprKind::Path(qself, path) = &expr.kind
&& path.is_potential_trivial_const_arg()
&& matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _)))
&& path.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
&& (tcx.features().min_generic_const_args()
|| matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _))))
{
let qpath = self.lower_qpath(
expr.id,
@ -2629,6 +2391,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
path,
ParamMode::Explicit,
AllowReturnTypeNotation::No,
// FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
@ -2636,25 +2399,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
return ConstArg {
hir_id: self.lower_node_id(anon.id),
kind: hir::ConstArgKind::Path(qpath),
span: self.lower_span(expr.span),
};
}
let lowered_anon = self.lower_anon_const_to_anon_const(anon, anon.value.span);
ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::Anon(lowered_anon),
span: self.lower_span(expr.span),
}
let lowered_anon = self.lower_anon_const_to_anon_const(anon);
ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Anon(lowered_anon) }
}
/// See [`hir::ConstArg`] for when to use this function vs
/// [`Self::lower_anon_const_to_const_arg`].
fn lower_anon_const_to_anon_const(
&mut self,
c: &AnonConst,
span: Span,
) -> &'hir hir::AnonConst {
fn lower_anon_const_to_anon_const(&mut self, c: &AnonConst) -> &'hir hir::AnonConst {
self.arena.alloc(self.with_new_scopes(c.value.span, |this| {
let def_id = this.local_def_id(c.id);
let hir_id = this.lower_node_id(c.id);
@ -2662,7 +2416,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
def_id,
hir_id,
body: this.lower_const_body(c.value.span, Some(&c.value)),
span: this.lower_span(span),
span: this.lower_span(c.value.span),
}
}))
}

View file

@ -399,6 +399,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
ExprKind::Lit(lit) => {
hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: false }
}
ExprKind::ConstBlock(c) => hir::PatExprKind::ConstBlock(self.lower_const_block(c)),
ExprKind::IncludedBytes(byte_sym) => hir::PatExprKind::Lit {
lit: respan(span, LitKind::ByteStr(*byte_sym, StrStyle::Cooked)),
negated: false,
@ -418,12 +419,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: true }
}
_ => {
let is_const_block = matches!(expr.kind, ExprKind::ConstBlock(_));
let pattern_from_macro = expr.is_approximately_pattern();
let guar = self.dcx().emit_err(ArbitraryExpressionInPattern {
span,
pattern_from_macro_note: pattern_from_macro,
const_block_in_pattern_help: is_const_block,
});
err(guar)
}
@ -444,9 +443,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let pat_hir_id = self.lower_node_id(pattern.id);
let node = match &pattern.kind {
TyPatKind::Range(e1, e2, Spanned { node: end, span }) => hir::TyPatKind::Range(
e1.as_deref()
.map(|e| self.lower_anon_const_to_const_arg_and_alloc(e))
.unwrap_or_else(|| {
e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)).unwrap_or_else(|| {
self.lower_ty_pat_range_end(
hir::LangItem::RangeMin,
span.shrink_to_lo(),
@ -455,7 +452,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}),
e2.as_deref()
.map(|e| match end {
RangeEnd::Included(..) => self.lower_anon_const_to_const_arg_and_alloc(e),
RangeEnd::Included(..) => self.lower_anon_const_to_const_arg(e),
RangeEnd::Excluded => self.lower_excluded_range_end(e),
})
.unwrap_or_else(|| {
@ -513,7 +510,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.arena.alloc(hir::ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::Anon(self.arena.alloc(anon_const)),
span,
})
}
@ -558,6 +554,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
})
});
let hir_id = self.next_id();
self.arena.alloc(hir::ConstArg { kind: hir::ConstArgKind::Anon(ct), hir_id, span })
self.arena.alloc(hir::ConstArg { kind: hir::ConstArgKind::Anon(ct), hir_id })
}
}

View file

@ -36,9 +36,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let qself = qself
.as_ref()
// Reject cases like `<impl Trait>::Assoc` and `<impl Trait as Trait>::Assoc`.
.map(|q| {
self.lower_ty_alloc(&q.ty, ImplTraitContext::Disallowed(ImplTraitPosition::Path))
});
.map(|q| self.lower_ty(&q.ty, ImplTraitContext::Disallowed(ImplTraitPosition::Path)));
let partial_res =
self.resolver.get_partial_res(id).unwrap_or_else(|| PartialRes::new(Res::Err));
@ -512,7 +510,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// we generally don't permit such things (see #51008).
let ParenthesizedArgs { span, inputs, inputs_span, output } = data;
let inputs = self.arena.alloc_from_iter(inputs.iter().map(|ty| {
self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitParam))
self.lower_ty_direct(ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitParam))
}));
let output_ty = match output {
// Only allow `impl Trait` in return position. i.e.:
@ -522,9 +520,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// ```
FnRetTy::Ty(ty) if matches!(itctx, ImplTraitContext::OpaqueTy { .. }) => {
if self.tcx.features().impl_trait_in_fn_trait_return() {
self.lower_ty_alloc(ty, itctx)
self.lower_ty(ty, itctx)
} else {
self.lower_ty_alloc(
self.lower_ty(
ty,
ImplTraitContext::FeatureGated(
ImplTraitPosition::FnTraitReturn,
@ -533,8 +531,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
)
}
}
FnRetTy::Ty(ty) => self
.lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn)),
FnRetTy::Ty(ty) => {
self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn))
}
FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
};
let args = smallvec![GenericArg::Type(

View file

@ -29,6 +29,7 @@ pub(crate) fn extern_abi_enabled(
})
}
#[allow(rustc::untranslatable_diagnostic)]
pub(crate) fn gate_unstable_abi(sess: &Session, features: &Features, span: Span, abi: ExternAbi) {
match extern_abi_enabled(features, span, abi) {
Ok(_) => (),
@ -95,11 +96,6 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> {
ExternAbi::RustCold => {
Err(UnstableAbi { abi, feature: sym::rust_cold_cc, explain: GateReason::Experimental })
}
ExternAbi::RustPreserveNone => Err(UnstableAbi {
abi,
feature: sym::rust_preserve_none_cc,
explain: GateReason::Experimental,
}),
ExternAbi::RustInvalid => {
Err(UnstableAbi { abi, feature: sym::rustc_attrs, explain: GateReason::ImplDetail })
}

View file

@ -13,6 +13,7 @@ rustc_attr_parsing = { path = "../rustc_attr_parsing" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_macros = { path = "../rustc_macros" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }

View file

@ -0,0 +1,340 @@
ast_passes_abi_cannot_be_coroutine =
functions with the {$abi} ABI cannot be `{$coroutine_kind_str}`
.suggestion = remove the `{$coroutine_kind_str}` keyword from this definition
ast_passes_abi_custom_safe_foreign_function =
foreign functions with the "custom" ABI cannot be safe
.suggestion = remove the `safe` keyword from this definition
ast_passes_abi_custom_safe_function =
functions with the "custom" ABI must be unsafe
.suggestion = add the `unsafe` keyword to this definition
ast_passes_abi_must_not_have_parameters_or_return_type=
invalid signature for `extern {$abi}` function
.note = functions with the {$abi} ABI cannot have any parameters or return type
.suggestion = remove the parameters and return type
ast_passes_abi_must_not_have_return_type=
invalid signature for `extern {$abi}` function
.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
ast_passes_assoc_fn_without_body =
associated function in `impl` without body
.suggestion = provide a definition for the function
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` {$context ->
[trait_impl] trait impls
[impl] impls
*[trait] 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
.label = auto trait cannot have generic parameters
.suggestion = remove the parameters
ast_passes_auto_items = auto traits cannot have associated items
.label = {ast_passes_auto_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_body_in_extern = incorrect `{$kind}` inside `extern` block
.cannot_have = cannot have a body
.invalid = the invalid body
.existing = `extern` blocks define existing foreign {$kind}s and {$kind}s inside of them cannot have a body
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_bad_naked_extern = `...` is not supported for `extern "{$abi}"` naked functions
.label = `extern "{$abi}"` because of this
.help = C-variadic function must have a compatible calling convention
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_c_variadic_not_supported = the `{$target}` target does not support c-variadic functions
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
ast_passes_const_and_coroutine = functions cannot be both `const` and `{$coroutine_kind}`
.const = `const` because of this
.coroutine = `{$coroutine_kind}` because of this
.label = {""}
ast_passes_const_auto_trait = auto traits cannot be const
.help = remove the `const` keyword
ast_passes_const_bound_trait_object = const trait bounds are not allowed in trait object types
ast_passes_const_without_body =
free constant item without body
.suggestion = provide a definition for the constant
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
.suggestion_path = if `{$trait_segment}::{$potential_assoc}` is an associated type you're trying to set, use the associated type binding syntax
.note = see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
ast_passes_extern_block_suggestion = if you meant to declare an externally defined function, use an `extern` block
ast_passes_extern_fn_qualifiers = functions in `extern` blocks cannot have `{$kw}` qualifier
.label = in this `extern` block
.suggestion = remove the `{$kw}` qualifier
ast_passes_extern_invalid_safety = items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
.suggestion = add `unsafe` to this `extern` block
ast_passes_extern_item_ascii = items in `extern` blocks cannot use non-ascii identifiers
.label = in this `extern` block
.note = this limitation may be lifted in the future; see issue #83942 <https://github.com/rust-lang/rust/issues/83942> for more information
ast_passes_extern_keyword_link = for more information, visit https://doc.rust-lang.org/std/keyword.extern.html
ast_passes_extern_types_cannot = `type`s inside `extern` blocks cannot have {$descr}
.suggestion = remove the {$remove_descr}
.label = `extern` block begins here
ast_passes_extern_without_abi = `extern` declarations without an explicit ABI are disallowed
.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
ast_passes_fieldless_union = unions cannot have zero fields
ast_passes_fn_body_extern = incorrect function inside `extern` block
.cannot_have = cannot have a body
.suggestion = remove the invalid body
.help = you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block
.label = `extern` blocks define existing foreign functions and functions inside of them cannot have a body
ast_passes_fn_param_c_var_args_not_last =
`...` must be the last argument of a C-variadic function
ast_passes_fn_param_doc_comment =
documentation comments cannot be applied to function parameters
.label = doc comments are not allowed here
ast_passes_fn_param_forbidden_attr =
allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters
ast_passes_fn_param_forbidden_self =
`self` parameter is only allowed in associated functions
.label = not semantically valid as function parameter
.note = associated functions are those in `impl` or `trait` definitions
ast_passes_fn_param_too_many =
function can not have more than {$max_num_args} arguments
ast_passes_fn_ptr_invalid_safety = function pointers cannot be declared with `safe` safety qualifier
.suggestion = remove safe from this item
ast_passes_fn_without_body =
free function without a body
.suggestion = provide a definition for the function
ast_passes_forbidden_bound =
bounds cannot be used in this context
ast_passes_forbidden_const_param =
late-bound const parameters cannot be used currently
ast_passes_forbidden_default =
`default` is only allowed on items in trait impls
.label = `default` because of this
ast_passes_forbidden_non_lifetime_param =
only lifetime parameters can be used in this context
ast_passes_generic_before_constraints = generic arguments must come before the first constraint
.constraints = {$constraint_len ->
[one] constraint
*[other] constraints
}
.args = generic {$args_len ->
[one] argument
*[other] arguments
}
.empty_string = {""},
.suggestion = move the {$constraint_len ->
[one] constraint
*[other] constraints
} after the generic {$args_len ->
[one] argument
*[other] arguments
}
ast_passes_generic_default_trailing = generic parameters with a default must be trailing
ast_passes_impl_fn_const =
redundant `const` fn marker in const impl
.parent_constness = this declares all associated functions implicitly const
.label = remove the `const`
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_item_invalid_safety = items outside of `unsafe extern {"{ }"}` cannot be declared with `safe` safety qualifier
.suggestion = remove safe from this item
ast_passes_item_underscore = `{$kind}` items in this context need a name
.label = `_` is not a valid name for this `{$kind}` item
ast_passes_match_arm_with_no_body =
`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
ast_passes_negative_bound_not_supported =
negative bounds are not supported
ast_passes_negative_bound_with_parenthetical_notation =
parenthetical notation may not be used for negative bounds
ast_passes_nested_impl_trait = nested `impl Trait` is not allowed
.outer = outer `impl Trait`
.inner = nested `impl Trait` here
ast_passes_nested_lifetimes = nested quantification of lifetimes
ast_passes_nomangle_ascii = `#[no_mangle]` requires ASCII identifier
ast_passes_obsolete_auto = `impl Trait for .. {"{}"}` is an obsolete syntax
.help = use `auto trait Trait {"{}"}` instead
ast_passes_out_of_order_params = {$param_ord} parameters must be declared prior to {$max_param} parameters
.suggestion = reorder the parameters: lifetimes, then consts and types
ast_passes_pattern_in_bodiless = patterns aren't allowed in functions without bodies
.label = pattern not allowed in function without body
ast_passes_pattern_in_fn_pointer = patterns aren't allowed in function pointer types
ast_passes_pattern_in_foreign = patterns aren't allowed in foreign function declarations
.label = pattern not allowed in foreign function
ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing syntax
.label = second `use<...>` here
ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc}
ast_passes_static_without_body =
free static item without body
.suggestion = provide a definition for the static
ast_passes_tilde_const_disallowed = `[const]` is not allowed here
.closure = closures cannot have `[const]` trait bounds
.function = this function is not `const`, so it cannot have `[const]` trait bounds
.trait = this trait is not `const`, so it cannot have `[const]` trait bounds
.trait_impl = this impl is not `const`, so it cannot have `[const]` trait bounds
.impl = inherent impls cannot have `[const]` trait bounds
.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
ast_passes_trait_fn_const =
functions in {$in_impl ->
[true] trait impls
*[false] traits
} cannot be declared const
.label = functions in {$in_impl ->
[true] trait impls
*[false] traits
} cannot be const
.const_context_label = this declares all associated functions implicitly const
.remove_const_sugg = remove the `const`{$requires_multiple_changes ->
[true] {" ..."}
*[false] {""}
}
.make_impl_const_sugg = ... and declare the impl to be const instead
.make_trait_const_sugg = ... and declare the trait to be const instead
ast_passes_trait_object_single_bound = only a single explicit lifetime bound is permitted
ast_passes_ty_alias_without_body =
free type alias without body
.suggestion = provide a definition for the type
ast_passes_unsafe_item = {$kind} cannot be declared unsafe
ast_passes_unsafe_negative_impl = negative impls cannot be unsafe
.negative = negative because of this
.unsafe = unsafe because of this
ast_passes_unsafe_static =
static items cannot be declared with `unsafe` safety qualifier outside of `extern` block
ast_passes_visibility_not_permitted =
visibility qualifiers are not permitted here
.enum_variant = enum variants and their fields always share the visibility of the enum they are in
.trait_impl = trait items always share the visibility of their trait
.individual_impl_items = place qualifiers on individual impl items instead
.individual_foreign_items = place qualifiers on individual foreign items instead
.remove_qualifier_sugg = remove the qualifier
ast_passes_where_clause_after_type_alias = where clauses are not allowed after the type for type aliases
.note = see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
.help = add `#![feature(lazy_type_alias)]` to the crate attributes to enable
ast_passes_where_clause_before_type_alias = where clauses are not allowed before the type for type aliases
.note = see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information
.remove_suggestion = remove this `where`
.move_suggestion = move it to the end of the type declaration

View file

@ -33,7 +33,7 @@ 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, UNUSED_VISIBILITIES,
PATTERNS_IN_FNS_WITHOUT_BODY,
};
use rustc_session::parse::feature_err;
use rustc_span::{Ident, Span, kw, sym};
@ -65,28 +65,6 @@ impl TraitOrImpl {
}
}
enum AllowDefault {
Yes,
No,
}
impl AllowDefault {
fn when(b: bool) -> Self {
if b { Self::Yes } else { Self::No }
}
}
enum AllowFinal {
Yes,
No,
}
impl AllowFinal {
fn when(b: bool) -> Self {
if b { Self::Yes } else { Self::No }
}
}
struct AstValidator<'a> {
sess: &'a Session,
features: &'a Features,
@ -422,18 +400,10 @@ impl<'a> AstValidator<'a> {
CanonAbi::C
| CanonAbi::Rust
| CanonAbi::RustCold
| CanonAbi::RustPreserveNone
| CanonAbi::Arm(_)
| CanonAbi::GpuKernel
| CanonAbi::X86(_) => { /* nothing to check */ }
CanonAbi::GpuKernel => {
// An `extern "gpu-kernel"` function cannot be `async` and/or `gen`.
self.reject_coroutine(abi, sig);
// An `extern "gpu-kernel"` function cannot return a value.
self.reject_return(abi, sig);
}
CanonAbi::Custom => {
// An `extern "custom"` function must be unsafe.
self.reject_safe_fn(abi, ctxt, sig);
@ -463,7 +433,18 @@ impl<'a> AstValidator<'a> {
self.dcx().emit_err(errors::AbiX86Interrupt { spans, param_count });
}
self.reject_return(abi, sig);
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,
});
}
} else {
// An `extern "interrupt"` function must have type `fn()`.
self.reject_params_or_return(abi, ident, sig);
@ -515,18 +496,6 @@ impl<'a> AstValidator<'a> {
}
}
fn reject_return(&self, abi: ExternAbi, sig: &FnSig) {
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 });
}
}
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
@ -585,33 +554,11 @@ impl<'a> AstValidator<'a> {
}
}
fn check_defaultness(
&self,
span: Span,
defaultness: Defaultness,
allow_default: AllowDefault,
allow_final: AllowFinal,
) {
match defaultness {
Defaultness::Default(def_span) if matches!(allow_default, AllowDefault::No) => {
fn check_defaultness(&self, span: Span, defaultness: Defaultness) {
if let Defaultness::Default(def_span) = defaultness {
let span = self.sess.source_map().guess_head_span(span);
self.dcx().emit_err(errors::ForbiddenDefault { span, def_span });
}
Defaultness::Final(def_span) if matches!(allow_final, AllowFinal::No) => {
let span = self.sess.source_map().guess_head_span(span);
self.dcx().emit_err(errors::ForbiddenFinal { span, def_span });
}
_ => (),
}
}
fn check_final_has_body(&self, item: &Item<AssocItemKind>, defaultness: Defaultness) {
if let AssocItemKind::Fn(box Fn { body: None, .. }) = &item.kind
&& let Defaultness::Final(def_span) = defaultness
{
let span = self.sess.source_map().guess_head_span(item.span);
self.dcx().emit_err(errors::ForbiddenFinalWithoutBody { span, def_span });
}
}
/// If `sp` ends with a semicolon, returns it as a `Span`
@ -742,11 +689,13 @@ impl<'a> AstValidator<'a> {
unreachable!("C variable argument list cannot be used in closures")
};
if let Const::Yes(_) = sig.header.constness
&& !self.features.enabled(sym::const_c_variadic)
{
let msg = format!("c-variadic const function definitions are unstable");
feature_err(&self.sess, sym::const_c_variadic, sig.span, msg).emit();
// 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: vec![const_span, variadic_param.span],
const_span,
variadic_span: variadic_param.span,
});
}
if let Some(coroutine_kind) = sig.header.coroutine_kind {
@ -1230,15 +1179,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
contract: _,
body,
define_opaque: _,
eii_impls,
},
) => {
self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No);
for EiiImpl { eii_macro_path, .. } in eii_impls {
self.visit_path(eii_macro_path);
}
self.check_defaultness(item.span, *defaultness);
let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic));
if body.is_none() && !is_intrinsic && !self.is_sdylib_interface {
@ -1370,14 +1314,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
ItemKind::Struct(ident, generics, vdata) => {
self.with_tilde_const(Some(TildeConstReason::Struct { span: item.span }), |this| {
// Scalable vectors can only be tuple structs
let is_scalable_vector =
item.attrs.iter().any(|attr| attr.has_name(sym::rustc_scalable_vector));
if is_scalable_vector && !matches!(vdata, VariantData::Tuple(..)) {
this.dcx()
.emit_err(errors::ScalableVectorNotTupleStruct { span: item.span });
}
match vdata {
VariantData::Struct { fields, .. } => {
this.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
@ -1403,26 +1339,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
});
}
ItemKind::Const(box ConstItem { defaultness, ident, rhs_kind, .. }) => {
self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No);
if !rhs_kind.has_expr() {
ItemKind::Const(box ConstItem { defaultness, rhs, .. }) => {
self.check_defaultness(item.span, *defaultness);
if rhs.is_none() {
self.dcx().emit_err(errors::ConstWithoutBody {
span: item.span,
replace_span: self.ending_semi_or_hi(item.span),
});
}
if ident.name == kw::Underscore
&& !matches!(item.vis.kind, VisibilityKind::Inherited)
&& ident.span.eq_ctxt(item.vis.span)
{
self.lint_buffer.buffer_lint(
UNUSED_VISIBILITIES,
item.id,
item.vis.span,
BuiltinLintDiag::UnusedVisibility(item.vis.span),
)
}
visit::walk_item(self, item);
}
ItemKind::Static(box StaticItem { expr, safety, .. }) => {
@ -1442,7 +1366,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
ItemKind::TyAlias(
ty_alias @ box TyAlias { defaultness, bounds, after_where_clause, ty, .. },
) => {
self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No);
self.check_defaultness(item.span, *defaultness);
if ty.is_none() {
self.dcx().emit_err(errors::TyAliasWithoutBody {
span: item.span,
@ -1472,7 +1396,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
match &fi.kind {
ForeignItemKind::Fn(box Fn { defaultness, ident, sig, body, .. }) => {
self.check_defaultness(fi.span, *defaultness, AllowDefault::No, AllowFinal::No);
self.check_defaultness(fi.span, *defaultness);
self.check_foreign_fn_bodyless(*ident, body.as_deref());
self.check_foreign_fn_headerless(sig.header);
self.check_foreign_item_ascii_only(*ident);
@ -1492,7 +1416,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
ty,
..
}) => {
self.check_defaultness(fi.span, *defaultness, AllowDefault::No, AllowFinal::No);
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, after_where_clause);
@ -1751,30 +1675,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_nomangle_item_asciionly(ident, item.span);
}
let defaultness = item.kind.defaultness();
self.check_defaultness(
item.span,
defaultness,
// `default` is allowed on all associated items in impls.
AllowDefault::when(matches!(ctxt, AssocCtxt::Impl { .. })),
// `final` is allowed on all associated *functions* in traits.
AllowFinal::when(
ctxt == AssocCtxt::Trait && matches!(item.kind, AssocItemKind::Fn(..)),
),
);
self.check_final_has_body(item, defaultness);
if ctxt == AssocCtxt::Trait || self.outer_trait_or_trait_impl.is_none() {
self.check_defaultness(item.span, item.kind.defaultness());
}
if let AssocCtxt::Impl { .. } = ctxt {
match &item.kind {
AssocItemKind::Const(box ConstItem { rhs_kind, .. }) => {
if !rhs_kind.has_expr() {
AssocItemKind::Const(box ConstItem { rhs: None, .. }) => {
self.dcx().emit_err(errors::AssocConstWithoutBody {
span: item.span,
replace_span: self.ending_semi_or_hi(item.span),
});
}
}
AssocItemKind::Fn(box Fn { body, .. }) => {
if body.is_none() && !self.is_sdylib_interface {
self.dcx().emit_err(errors::AssocFnWithoutBody {

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,5 @@
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
use rustc_ast::{self as ast, AttrVec, NodeId, PatKind, attr, token};
use rustc_errors::msg;
use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features};
use rustc_session::Session;
use rustc_session::parse::{feature_err, feature_warn};
@ -14,11 +13,15 @@ use crate::errors;
macro_rules! gate {
($visitor:expr, $feature:ident, $span:expr, $explain:expr) => {{
if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) {
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
feature_err(&$visitor.sess, sym::$feature, $span, $explain).emit();
}
}};
($visitor:expr, $feature:ident, $span:expr, $explain:expr, $help:expr) => {{
if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) {
// FIXME: make this translatable
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
feature_err(&$visitor.sess, sym::$feature, $span, $explain).with_help($help).emit();
}
}};
@ -28,11 +31,13 @@ macro_rules! gate {
macro_rules! gate_alt {
($visitor:expr, $has_feature:expr, $name:expr, $span:expr, $explain:expr) => {{
if !$has_feature && !$span.allows_unstable($name) {
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
feature_err(&$visitor.sess, $name, $span, $explain).emit();
}
}};
($visitor:expr, $has_feature:expr, $name:expr, $span:expr, $explain:expr, $notes: expr) => {{
if !$has_feature && !$span.allows_unstable($name) {
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
let mut diag = feature_err(&$visitor.sess, $name, $span, $explain);
for note in $notes {
diag.note(*note);
@ -125,7 +130,7 @@ impl<'a> PostExpansionVisitor<'a> {
&self,
non_lifetime_binders,
non_lt_param_spans,
msg!("only lifetime parameters can be used in this context")
crate::fluent_generated::ast_passes_forbidden_non_lifetime_param
);
// FIXME(non_lifetime_binders): Const bound params are pretty broken.
@ -155,7 +160,7 @@ impl<'a> PostExpansionVisitor<'a> {
impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
fn visit_attribute(&mut self, attr: &ast::Attribute) {
let attr_info = attr.name().and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name));
let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
// Check feature gates for built-in attributes.
if let Some(BuiltinAttribute {
gate: AttributeGate::Gated { feature, message, check, notes, .. },
@ -249,14 +254,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
ast::ItemKind::TyAlias(box ast::TyAlias { ty: Some(ty), .. }) => {
self.check_impl_trait(ty, false)
}
ast::ItemKind::Const(box ast::ConstItem {
rhs_kind: ast::ConstItemRhsKind::TypeConst { .. },
..
}) => {
// Make sure this is only allowed if the feature gate is enabled.
// #![feature(min_generic_const_args)]
gate!(&self, min_generic_const_args, i.span, "top-level `type const` are unstable");
}
_ => {}
}
@ -304,12 +301,17 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
visit::walk_ty(self, ty)
}
fn visit_where_predicate_kind(&mut self, kind: &'a ast::WherePredicateKind) {
if let ast::WherePredicateKind::BoundPredicate(bound) = kind {
fn visit_generics(&mut self, g: &'a ast::Generics) {
for predicate in &g.where_clause.predicates {
match &predicate.kind {
ast::WherePredicateKind::BoundPredicate(bound_pred) => {
// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`).
self.check_late_bound_lifetime_defs(&bound.bound_generic_params);
self.check_late_bound_lifetime_defs(&bound_pred.bound_generic_params);
}
visit::walk_where_predicate_kind(self, kind);
_ => {}
}
}
visit::walk_generics(self, g);
}
fn visit_fn_ret_ty(&mut self, ret_ty: &'a ast::FnRetTy) {
@ -337,13 +339,9 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
fn visit_expr(&mut self, e: &'a ast::Expr) {
match e.kind {
ast::ExprKind::TryBlock(_, None) => {
// `try { ... }` is old and is only gated post-expansion here.
ast::ExprKind::TryBlock(_) => {
gate!(&self, try_blocks, e.span, "`try` expression is experimental");
}
ast::ExprKind::TryBlock(_, Some(_)) => {
// `try_blocks_heterogeneous` is new, and gated pre-expansion instead.
}
ast::ExprKind::Lit(token::Lit {
kind: token::LitKind::Float | token::LitKind::Integer,
suffix,
@ -426,20 +424,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
}
false
}
ast::AssocItemKind::Const(box ast::ConstItem {
rhs_kind: ast::ConstItemRhsKind::TypeConst { .. },
..
}) => {
// Make sure this is only allowed if the feature gate is enabled.
// #![feature(min_generic_const_args)]
gate!(
&self,
min_generic_const_args,
i.span,
"associated `type const` are unstable"
);
false
}
_ => false,
};
if let ast::Defaultness::Default(_) = i.kind.defaultness() {
@ -459,7 +443,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
maybe_stage_features(sess, features, krate);
check_incompatible_features(sess, features);
check_dependent_features(sess, features);
check_new_solver_banned_features(sess, features);
let mut visitor = PostExpansionVisitor { sess, features };
@ -505,6 +488,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
&& (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks))
&& (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr))
{
#[allow(rustc::untranslatable_diagnostic)]
// Emit yield_expr as the error, since that will be sufficient. You can think of it
// as coroutines and gen_blocks imply yield_expr.
feature_err(&visitor.sess, sym::yield_expr, *span, "yield syntax is experimental")
@ -518,7 +502,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
half_open_range_patterns_in_slices,
"half-open range patterns in slices are unstable"
);
gate_all!(try_blocks_heterogeneous, "`try bikeshed` expression is experimental");
gate_all!(associated_const_equality, "associated const equality is incomplete");
gate_all!(yeet_expr, "`do yeet` expression is experimental");
gate_all!(const_closures, "const closures are experimental");
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
@ -530,44 +514,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
gate_all!(postfix_match, "postfix match is experimental");
gate_all!(mut_ref, "mutable by-reference bindings are experimental");
gate_all!(min_generic_const_args, "unbraced const blocks as const args are experimental");
// associated_const_equality is stabilized as part of min_generic_const_args
if let Some(spans) = spans.get(&sym::associated_const_equality) {
for span in spans {
if !visitor.features.min_generic_const_args()
&& !span.allows_unstable(sym::min_generic_const_args)
{
feature_err(
&visitor.sess,
sym::min_generic_const_args,
*span,
"associated const equality is incomplete",
)
.emit();
}
}
}
// `mgca_type_const_syntax` is part of `min_generic_const_args` so either
// or both are enabled we don't need to emit a feature error.
if let Some(spans) = spans.get(&sym::mgca_type_const_syntax) {
for span in spans {
if visitor.features.min_generic_const_args()
|| visitor.features.mgca_type_const_syntax()
|| span.allows_unstable(sym::min_generic_const_args)
|| span.allows_unstable(sym::mgca_type_const_syntax)
{
continue;
}
feature_err(
&visitor.sess,
sym::min_generic_const_args,
*span,
"`type const` syntax is experimental",
)
.emit();
}
}
gate_all!(global_registration, "global registration is experimental");
gate_all!(return_type_notation, "return type notation is experimental");
gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
@ -579,8 +525,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(super_let, "`super let` is experimental");
gate_all!(frontmatter, "frontmatters are experimental");
gate_all!(coroutines, "coroutine syntax is experimental");
gate_all!(const_block_items, "const block items are experimental");
gate_all!(final_associated_functions, "`final` on trait functions is experimental");
if !visitor.features.never_patterns() {
if let Some(spans) = spans.get(&sym::never_patterns) {
@ -595,6 +539,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
if let Ok(snippet) = sm.span_to_snippet(span)
&& snippet == "!"
{
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
feature_err(sess, sym::never_patterns, span, "`!` patterns are experimental")
.emit();
} else {
@ -691,27 +636,6 @@ fn check_incompatible_features(sess: &Session, features: &Features) {
}
}
fn check_dependent_features(sess: &Session, features: &Features) {
for &(parent, children) in
rustc_feature::DEPENDENT_FEATURES.iter().filter(|(parent, _)| features.enabled(*parent))
{
if children.iter().any(|f| !features.enabled(*f)) {
let parent_span = features
.enabled_features_iter_stable_order()
.find_map(|(name, span)| (name == parent).then_some(span))
.unwrap();
// FIXME: should probably format this in fluent instead of here
let missing = children
.iter()
.filter(|f| !features.enabled(**f))
.map(|s| format!("`{}`", s.as_str()))
.intersperse(String::from(", "))
.collect();
sess.dcx().emit_err(errors::MissingDependentFeatures { parent_span, parent, missing });
}
}
}
fn check_new_solver_banned_features(sess: &Session, features: &Features) {
if !sess.opts.unstable_opts.next_solver.globally {
return;

View file

@ -5,10 +5,11 @@
// tidy-alphabetical-start
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(iter_is_partitioned)]
// tidy-alphabetical-end
pub mod ast_validation;
mod errors;
pub mod feature_gate;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }

View file

@ -33,9 +33,6 @@ pub fn where_bound_predicate_to_string(where_bound_predicate: &ast::WhereBoundPr
State::new().where_bound_predicate_to_string(where_bound_predicate)
}
/// # Panics
///
/// Panics if `pat.kind` is `PatKind::Missing`.
pub fn pat_to_string(pat: &ast::Pat) -> String {
State::new().pat_to_string(pat)
}

View file

@ -10,7 +10,7 @@ use std::borrow::Cow;
use std::sync::Arc;
use rustc_ast::attr::AttrIdGenerator;
use rustc_ast::token::{self, CommentKind, Delimiter, DocFragmentKind, 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};
@ -381,24 +381,15 @@ fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {
}
pub fn doc_comment_to_string(
fragment_kind: DocFragmentKind,
comment_kind: CommentKind,
attr_style: ast::AttrStyle,
data: Symbol,
) -> String {
match fragment_kind {
DocFragmentKind::Sugared(comment_kind) => match (comment_kind, attr_style) {
match (comment_kind, attr_style) {
(CommentKind::Line, ast::AttrStyle::Outer) => format!("///{data}"),
(CommentKind::Line, ast::AttrStyle::Inner) => format!("//!{data}"),
(CommentKind::Block, ast::AttrStyle::Outer) => format!("/**{data}*/"),
(CommentKind::Block, ast::AttrStyle::Inner) => format!("/*!{data}*/"),
},
DocFragmentKind::Raw(_) => {
format!(
"#{}[doc = {:?}]",
if attr_style == ast::AttrStyle::Inner { "!" } else { "" },
data.to_string(),
)
}
}
}
@ -674,11 +665,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
self.word("]");
}
ast::AttrKind::DocComment(comment_kind, data) => {
self.word(doc_comment_to_string(
DocFragmentKind::Sugared(*comment_kind),
attr.style,
*data,
));
self.word(doc_comment_to_string(*comment_kind, attr.style, *data));
self.hardbreak()
}
}
@ -694,7 +681,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
}
ast::Safety::Default | ast::Safety::Safe(_) => {}
}
match &item.args.unparsed_ref().expect("Parsed attributes are never printed") {
match &item.args {
AttrArgs::Delimited(DelimArgs { dspan: _, delim, tokens }) => self.print_mac_common(
Some(MacHeader::Path(&item.path)),
false,
@ -865,17 +852,6 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
sp: Span,
print_visibility: impl FnOnce(&mut Self),
) {
if let Some(eii_decl) = &macro_def.eii_declaration {
self.word("#[eii_declaration(");
self.print_path(&eii_decl.foreign_item, false, 0);
if eii_decl.impl_unsafe {
self.word(",");
self.space();
self.word("unsafe");
}
self.word(")]");
self.hardbreak();
}
let (kw, has_bang) = if macro_def.macro_rules {
("macro_rules", true)
} else {
@ -1053,8 +1029,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
/* Other */
token::DocComment(comment_kind, attr_style, data) => {
doc_comment_to_string(DocFragmentKind::Sugared(comment_kind), attr_style, data)
.into()
doc_comment_to_string(comment_kind, attr_style, data).into()
}
token::Eof => "<eof>".into(),
}
@ -1961,8 +1936,7 @@ impl<'a> State<'a> {
}
fn print_lifetime(&mut self, lifetime: ast::Lifetime) {
self.word(lifetime.ident.name.to_string());
self.ann_post(lifetime.ident)
self.print_name(lifetime.ident.name)
}
fn print_lifetime_bounds(&mut self, bounds: &ast::GenericBounds) {
@ -2174,15 +2148,6 @@ impl<'a> State<'a> {
fn print_meta_item(&mut self, item: &ast::MetaItem) {
let ib = self.ibox(INDENT_UNIT);
match item.unsafety {
ast::Safety::Unsafe(_) => {
self.word("unsafe");
self.popen();
}
ast::Safety::Default | ast::Safety::Safe(_) => {}
}
match &item.kind {
ast::MetaItemKind::Word => self.print_path(&item.path, false, 0),
ast::MetaItemKind::NameValue(value) => {
@ -2198,12 +2163,6 @@ impl<'a> State<'a> {
self.pclose();
}
}
match item.unsafety {
ast::Safety::Unsafe(_) => self.pclose(),
ast::Safety::Default | ast::Safety::Safe(_) => {}
}
self.end(ib);
}

View file

@ -818,15 +818,10 @@ impl<'a> State<'a> {
);
self.word("?")
}
ast::ExprKind::TryBlock(blk, opt_ty) => {
ast::ExprKind::TryBlock(blk) => {
let cb = self.cbox(0);
let ib = self.ibox(0);
self.word_nbsp("try");
if let Some(ty) = opt_ty {
self.word_nbsp("bikeshed");
self.print_type(ty);
self.space();
}
self.print_block_with_attrs(blk, attrs, cb, ib)
}
ast::ExprKind::UnsafeBinderCast(kind, expr, ty) => {

View file

@ -1,6 +1,6 @@
use ast::StaticItem;
use itertools::{Itertools, Position};
use rustc_ast::{self as ast, EiiImpl, ModKind, Safety, TraitAlias};
use rustc_ast::{self as ast, ModKind, TraitAlias};
use rustc_span::Ident;
use crate::pp::BoxMarker;
@ -51,7 +51,7 @@ impl<'a> State<'a> {
expr.as_deref(),
vis,
*safety,
ast::Defaultness::Implicit,
ast::Defaultness::Final,
define_opaque.as_deref(),
),
ast::ForeignItemKind::TyAlias(box ast::TyAlias {
@ -201,27 +201,16 @@ impl<'a> State<'a> {
body.as_deref(),
&item.vis,
ast::Safety::Default,
ast::Defaultness::Implicit,
ast::Defaultness::Final,
define_opaque.as_deref(),
);
}
ast::ItemKind::ConstBlock(ast::ConstBlockItem { id: _, span: _, block }) => {
let ib = self.ibox(INDENT_UNIT);
self.word("const");
self.nbsp();
{
let cb = self.cbox(0);
let ib = self.ibox(0);
self.print_block_with_attrs(block, &[], cb, ib);
}
self.end(ib);
}
ast::ItemKind::Const(box ast::ConstItem {
defaultness,
ident,
generics,
ty,
rhs_kind,
rhs,
define_opaque,
}) => {
self.print_item_const(
@ -229,7 +218,7 @@ impl<'a> State<'a> {
None,
generics,
ty,
rhs_kind.expr(),
rhs.as_ref().map(|ct| ct.expr()),
&item.vis,
ast::Safety::Default,
*defaultness,
@ -573,7 +562,7 @@ impl<'a> State<'a> {
ident,
generics,
ty,
rhs_kind,
rhs,
define_opaque,
}) => {
self.print_item_const(
@ -581,7 +570,7 @@ impl<'a> State<'a> {
None,
generics,
ty,
rhs_kind.expr(),
rhs.as_ref().map(|ct| ct.expr()),
vis,
ast::Safety::Default,
*defaultness,
@ -682,25 +671,10 @@ impl<'a> State<'a> {
}
fn print_fn_full(&mut self, vis: &ast::Visibility, attrs: &[ast::Attribute], func: &ast::Fn) {
let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque, eii_impls } =
func;
let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque } = func;
self.print_define_opaques(define_opaque.as_deref());
for EiiImpl { eii_macro_path, impl_safety, .. } in eii_impls {
self.word("#[");
if let Safety::Unsafe(..) = impl_safety {
self.word("unsafe");
self.popen();
}
self.print_path(eii_macro_path, false, 0);
if let Safety::Unsafe(..) = impl_safety {
self.pclose();
}
self.word("]");
self.hardbreak();
}
let body_cb_ib = body.as_ref().map(|body| (body, self.head("")));
self.print_visibility(vis);

View file

@ -8,9 +8,9 @@ edition = "2024"
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_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" }

View file

@ -0,0 +1,268 @@
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
attr_parsing_deprecated_item_suggestion =
suggestions on deprecated items are unstable
.help = add `#![feature(deprecated_suggestion)]` to the crate root
.note = see #94785 for more details
attr_parsing_empty_attribute =
unused 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
attr_parsing_expected_single_version_literal =
expected single version literal
attr_parsing_expected_version_literal =
expected a version literal
attr_parsing_expects_feature_list =
`{$name}` expects a list of feature names
attr_parsing_expects_features =
`{$name}` expects feature names
attr_parsing_ill_formed_attribute_input = {$num_suggestions ->
[1] attribute must be of the form {$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
attr_parsing_incorrect_repr_format_expect_literal_integer =
incorrect `repr(align)` attribute format: `align` expects a literal integer as argument
attr_parsing_incorrect_repr_format_generic =
incorrect `repr({$repr_arg})` attribute format
.suggestion = use parentheses instead
attr_parsing_incorrect_repr_format_packed_expect_integer =
incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument
attr_parsing_incorrect_repr_format_packed_one_or_zero_arg =
incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all
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
.empty = cannot parse integer from empty string
.invalid_digit = invalid digit found in 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}`
attr_parsing_invalid_repr_align_need_arg =
invalid `repr(align)` attribute: `align` needs an argument
.suggestion = supply an argument here
attr_parsing_invalid_repr_generic =
invalid `repr({$repr_arg})` attribute: {$error_part}
attr_parsing_invalid_repr_hint_no_paren =
invalid representation hint: `{$name}` does not take a parenthesized argument list
attr_parsing_invalid_repr_hint_no_value =
invalid representation hint: `{$name}` does not take a 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'
attr_parsing_missing_issue =
missing 'issue'
attr_parsing_missing_note =
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
attr_parsing_naked_functions_incompatible_attribute =
attribute incompatible with `#[unsafe(naked)]`
.label = the `{$attr}` attribute is incompatible with `#[unsafe(naked)]`
.naked_attribute = function marked with `#[unsafe(naked)]` here
attr_parsing_non_ident_feature =
'feature' is not an identifier
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
attr_parsing_rustc_allowed_unstable_pairing =
`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
attr_parsing_rustc_promotable_pairing =
`rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute
attr_parsing_soft_no_args =
`soft` should not have any arguments
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}
attr_parsing_unknown_version_literal =
unknown version literal format, assuming it refers to a future version
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
.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 =
literal in `cfg` predicate value must be a boolean
attr_parsing_unsupported_literal_cfg_string =
literal in `cfg` predicate value must be a string
attr_parsing_unsupported_literal_generic =
unsupported literal
attr_parsing_unsupported_literal_suggestion =
consider removing the prefix
attr_parsing_unused_duplicate =
unused attribute
.suggestion = remove this attribute
.note = attribute also specified here
.warn = {-attr_parsing_previously_accepted}
attr_parsing_unused_multiple =
multiple `{$name}` attributes
.suggestion = remove this attribute
.note = attribute also specified here
-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

@ -17,9 +17,9 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
]);
const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]);
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> {
parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0])
.into_iter()
@ -29,7 +29,7 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
pub(crate) struct UnstableFeatureBoundParser;
impl<S: Stage> CombineAttributeParser<S> for UnstableFeatureBoundParser {
const PATH: &[rustc_span::Symbol] = &[sym::unstable_feature_bound];
const PATH: &'static [rustc_span::Symbol] = &[sym::unstable_feature_bound];
type Item = (Symbol, Span);
const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::UnstableFeatureBound(items);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
@ -39,9 +39,9 @@ impl<S: Stage> CombineAttributeParser<S> for UnstableFeatureBoundParser {
]);
const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]);
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> {
if !cx.features().staged_api() {
cx.emit_err(session_diagnostics::StabilityOutsideStd { span: cx.attr_span });
@ -57,26 +57,27 @@ impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];
type Item = Symbol;
const CONVERT: ConvertFn<Self::Item> =
|items, first_span| AttributeKind::RustcAllowConstFnUnstable(items, first_span);
|items, first_span| AttributeKind::AllowConstFnUnstable(items, first_span);
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(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c {
parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0])
}
}
fn parse_unstable<S: Stage>(
cx: &AcceptContext<'_, '_, S>,
args: &ArgParser,
args: &ArgParser<'_>,
symbol: Symbol,
) -> impl IntoIterator<Item = Symbol> {
let mut res = Vec::new();

View file

@ -2,18 +2,16 @@ use std::convert::identity;
use rustc_ast::token::Delimiter;
use rustc_ast::tokenstream::DelimSpan;
use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, ast, token};
use rustc_errors::{Applicability, PResult, msg};
use rustc_feature::{
AttrSuggestionStyle, AttributeTemplate, Features, GatedCfg, find_gated_cfg, template,
};
use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, NodeId, ast, token};
use rustc_errors::{Applicability, PResult};
use rustc_feature::{AttrSuggestionStyle, AttributeTemplate, Features, template};
use rustc_hir::attrs::CfgEntry;
use rustc_hir::lints::AttributeLintKind;
use rustc_hir::{AttrPath, RustcVersion, Target};
use rustc_parse::parser::{ForceCollect, Parser, Recovery};
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::{ParseSess, feature_err};
use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
@ -25,7 +23,10 @@ use crate::session_diagnostics::{
AttributeParseError, AttributeParseErrorReason, CfgAttrBadDelim, MetaBadDelimSugg,
ParsedDescription,
};
use crate::{AttributeParser, parse_version, session_diagnostics};
use crate::{
AttributeParser, CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics,
try_gate_cfg,
};
pub const CFG_TEMPLATE: AttributeTemplate = template!(
List: &["predicate"],
@ -37,12 +38,12 @@ const CFG_ATTR_TEMPLATE: AttributeTemplate = template!(
"https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute"
);
pub fn parse_cfg<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
pub fn parse_cfg<'c, S: Stage>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> Option<CfgEntry> {
let ArgParser::List(list) = args else {
cx.expected_list(cx.attr_span, args);
cx.expected_list(cx.attr_span);
return None;
};
let Some(single) = list.single() else {
@ -54,7 +55,7 @@ pub fn parse_cfg<S: Stage>(
pub fn parse_cfg_entry<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
item: &MetaItemOrLitParser,
item: &MetaItemOrLitParser<'_>,
) -> Result<CfgEntry, ErrorGuaranteed> {
Ok(match item {
MetaItemOrLitParser::MetaItemParser(meta) => match meta.args() {
@ -94,12 +95,13 @@ pub fn parse_cfg_entry<S: Stage>(
LitKind::Bool(b) => CfgEntry::Bool(b, lit.span),
_ => return Err(cx.expected_identifier(lit.span)),
},
MetaItemOrLitParser::Err(_, err) => return Err(*err),
})
}
fn parse_cfg_entry_version<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
list: &MetaItemListParser,
list: &MetaItemListParser<'_>,
meta_span: Span,
) -> Result<CfgEntry, ErrorGuaranteed> {
try_gate_cfg(sym::version, meta_span, cx.sess(), cx.features_option());
@ -131,7 +133,7 @@ fn parse_cfg_entry_version<S: Stage>(
fn parse_cfg_entry_target<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
list: &MetaItemListParser,
list: &MetaItemListParser<'_>,
meta_span: Span,
) -> Result<CfgEntry, ErrorGuaranteed> {
if let Some(features) = cx.features_option()
@ -141,7 +143,7 @@ fn parse_cfg_entry_target<S: Stage>(
cx.sess(),
sym::cfg_target_compact,
meta_span,
msg!("compact `cfg(target(..))` is experimental and subject to change"),
fluent_generated::attr_parsing_unstable_cfg_target_compact,
)
.emit();
}
@ -172,7 +174,7 @@ fn parse_cfg_entry_target<S: Stage>(
Ok(CfgEntry::All(result, list.span))
}
pub(crate) fn parse_name_value<S: Stage>(
fn parse_name_value<S: Stage>(
name: Symbol,
name_span: Span,
value: Option<&NameValueParser>,
@ -193,46 +195,43 @@ pub(crate) fn parse_name_value<S: Stage>(
}
};
match cx.sess.psess.check_config.expecteds.get(&name) {
Some(ExpectedValues::Some(values)) if !values.contains(&value.map(|(v, _)| v)) => cx
.emit_lint(
UNEXPECTED_CFGS,
AttributeLintKind::UnexpectedCfgValue((name, name_span), value),
span,
),
None if cx.sess.psess.check_config.exhaustive_names => cx.emit_lint(
UNEXPECTED_CFGS,
AttributeLintKind::UnexpectedCfgName((name, name_span), value),
span,
),
_ => { /* not unexpected */ }
}
Ok(CfgEntry::NameValue { name, value: value.map(|(v, _)| v), span })
Ok(CfgEntry::NameValue { name, name_span, value, span })
}
pub fn eval_config_entry(sess: &Session, cfg_entry: &CfgEntry) -> EvalConfigResult {
pub fn eval_config_entry(
sess: &Session,
cfg_entry: &CfgEntry,
id: NodeId,
emit_lints: ShouldEmit,
) -> EvalConfigResult {
match cfg_entry {
CfgEntry::All(subs, ..) => {
let mut all = None;
for sub in subs {
let res = eval_config_entry(sess, sub);
let res = eval_config_entry(sess, sub, id, emit_lints);
// We cannot short-circuit because `eval_config_entry` emits some lints
if !res.as_bool() {
return res;
all.get_or_insert(res);
}
}
EvalConfigResult::True
all.unwrap_or_else(|| EvalConfigResult::True)
}
CfgEntry::Any(subs, span) => {
let mut any = None;
for sub in subs {
let res = eval_config_entry(sess, sub);
let res = eval_config_entry(sess, sub, id, emit_lints);
// We cannot short-circuit because `eval_config_entry` emits some lints
if res.as_bool() {
return res;
any.get_or_insert(res);
}
}
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span }
any.unwrap_or_else(|| EvalConfigResult::False {
reason: cfg_entry.clone(),
reason_span: *span,
})
}
CfgEntry::Not(sub, span) => {
if eval_config_entry(sess, sub).as_bool() {
if eval_config_entry(sess, sub, id, emit_lints).as_bool() {
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span }
} else {
EvalConfigResult::True
@ -245,8 +244,32 @@ pub fn eval_config_entry(sess: &Session, cfg_entry: &CfgEntry) -> EvalConfigResu
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span }
}
}
CfgEntry::NameValue { name, value, span } => {
if sess.psess.config.contains(&(*name, *value)) {
CfgEntry::NameValue { name, name_span, value, span } => {
if let ShouldEmit::ErrorsAndLints = emit_lints {
match sess.psess.check_config.expecteds.get(name) {
Some(ExpectedValues::Some(values))
if !values.contains(&value.map(|(v, _)| v)) =>
{
id.emit_span_lint(
sess,
UNEXPECTED_CFGS,
*span,
BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value),
);
}
None if sess.psess.check_config.exhaustive_names => {
id.emit_span_lint(
sess,
UNEXPECTED_CFGS,
*span,
BuiltinLintDiag::UnexpectedCfgName((*name, *name_span), *value),
);
}
_ => { /* not unexpected */ }
}
}
if sess.psess.config.contains(&(*name, value.map(|(v, _)| v))) {
EvalConfigResult::True
} else {
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span }
@ -293,9 +316,11 @@ pub fn parse_cfg_attr(
sess: &Session,
features: Option<&Features>,
) -> Option<(CfgEntry, Vec<(AttrItem, Span)>)> {
match cfg_attr.get_normal_item().args.unparsed_ref().unwrap() {
ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, tokens }) if !tokens.is_empty() => {
check_cfg_attr_bad_delim(&sess.psess, *dspan, *delim);
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)
}) {
@ -319,7 +344,7 @@ pub fn parse_cfg_attr(
}
_ => {
let (span, reason) = if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) =
cfg_attr.get_normal_item().args.unparsed_ref()?
cfg_attr.get_normal_item().args
{
(dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument)
} else {
@ -360,10 +385,7 @@ fn parse_cfg_attr_internal<'a>(
) -> PResult<'a, (CfgEntry, Vec<(ast::AttrItem, Span)>)> {
// Parse cfg predicate
let pred_start = parser.token.span;
let meta = MetaItemOrLitParser::parse_single(
parser,
ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
)?;
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(
@ -371,14 +393,19 @@ fn parse_cfg_attr_internal<'a>(
attribute.span,
attribute.get_normal_item().span(),
attribute.style,
AttrPath { segments: attribute.path().into_boxed_slice(), span: attribute.span },
AttrPath {
segments: attribute
.ident_path()
.expect("cfg_attr is not a doc comment")
.into_boxed_slice(),
span: attribute.span,
},
Some(attribute.get_normal_item().unsafety),
ParsedDescription::Attribute,
pred_span,
CRATE_NODE_ID,
Target::Crate,
features,
ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
ShouldEmit::ErrorsAndLints,
&meta,
parse_cfg_entry,
&CFG_ATTR_TEMPLATE,
@ -407,18 +434,3 @@ fn parse_cfg_attr_internal<'a>(
Ok((cfg_predicate, expanded_attrs))
}
fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) {
let gate = find_gated_cfg(|sym| sym == name);
if let (Some(feats), Some(gated_cfg)) = (features, gate) {
gate_cfg(gated_cfg, span, sess, feats);
}
}
fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) {
let (cfg, feature, has_feature) = gated_cfg;
if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
let explain = format!("`cfg({cfg})` is experimental and subject to change");
feature_err(sess, *feature, cfg_span, explain).emit();
}
}

View file

@ -0,0 +1,250 @@
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
use rustc_ast_pretty::pprust;
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;
use rustc_session::lint::{BuiltinLintDiag, Lint};
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, sym};
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
use crate::{fluent_generated, parse_version};
/// Emitter of a builtin lint from `cfg_matches`.
///
/// Used to support emitting a lint (currently on check-cfg), either:
/// - as an early buffered lint (in `rustc`)
/// - or has a "normal" lint from HIR (in `rustdoc`)
pub trait CfgMatchesLintEmitter {
fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag);
}
impl CfgMatchesLintEmitter for NodeId {
fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag) {
sess.psess.buffer_lint(lint, sp, *self, diag);
}
}
#[derive(Clone, Debug)]
pub struct Condition {
pub name: Symbol,
pub name_span: Span,
pub value: Option<Symbol>,
pub value_span: Option<Span>,
pub span: Span,
}
/// Tests if a cfg-pattern matches the cfg set
pub fn cfg_matches(
cfg: &MetaItemInner,
sess: &Session,
lint_emitter: impl CfgMatchesLintEmitter,
features: Option<&Features>,
) -> bool {
eval_condition(cfg, sess, features, &mut |cfg| {
try_gate_cfg(cfg.name, cfg.span, sess, features);
match sess.psess.check_config.expecteds.get(&cfg.name) {
Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
lint_emitter.emit_span_lint(
sess,
UNEXPECTED_CFGS,
cfg.span,
BuiltinLintDiag::UnexpectedCfgValue(
(cfg.name, cfg.name_span),
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
),
);
}
None if sess.psess.check_config.exhaustive_names => {
lint_emitter.emit_span_lint(
sess,
UNEXPECTED_CFGS,
cfg.span,
BuiltinLintDiag::UnexpectedCfgName(
(cfg.name, cfg.name_span),
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
),
);
}
_ => { /* not unexpected */ }
}
sess.psess.config.contains(&(cfg.name, cfg.value))
})
}
pub fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) {
let gate = find_gated_cfg(|sym| sym == name);
if let (Some(feats), Some(gated_cfg)) = (features, gate) {
gate_cfg(gated_cfg, span, sess, feats);
}
}
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) {
let (cfg, feature, has_feature) = gated_cfg;
if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
let explain = format!("`cfg({cfg})` is experimental and subject to change");
feature_err(sess, *feature, cfg_span, explain).emit();
}
}
/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
/// evaluate individual items.
pub fn eval_condition(
cfg: &MetaItemInner,
sess: &Session,
features: Option<&Features>,
eval: &mut impl FnMut(Condition) -> bool,
) -> bool {
let dcx = sess.dcx();
let cfg = match cfg {
MetaItemInner::MetaItem(meta_item) => meta_item,
MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
return *b;
}
_ => {
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
span: cfg.span(),
reason: UnsupportedLiteralReason::CfgBoolean,
is_bytestr: false,
start_point_span: sess.source_map().start_point(cfg.span()),
});
return false;
}
};
match &cfg.kind {
MetaItemKind::List(mis) if cfg.has_name(sym::version) => {
try_gate_cfg(sym::version, cfg.span, sess, features);
let (min_version, span) = match &mis[..] {
[MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => {
(sym, span)
}
[
MetaItemInner::Lit(MetaItemLit { span, .. })
| MetaItemInner::MetaItem(MetaItem { span, .. }),
] => {
dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span });
return false;
}
[..] => {
dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral {
span: cfg.span,
});
return false;
}
};
let Some(min_version) = parse_version(*min_version) else {
dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span });
return false;
};
// See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
if sess.psess.assume_incomplete_release {
RustcVersion::current_overridable() > min_version
} else {
RustcVersion::current_overridable() >= min_version
}
}
MetaItemKind::List(mis) => {
for mi in mis.iter() {
if mi.meta_item_or_bool().is_none() {
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
span: mi.span(),
reason: UnsupportedLiteralReason::Generic,
is_bytestr: false,
start_point_span: sess.source_map().start_point(mi.span()),
});
return false;
}
}
// The unwraps below may look dangerous, but we've already asserted
// that they won't fail with the loop above.
match cfg.name() {
Some(sym::any) => mis
.iter()
// We don't use any() here, because we want to evaluate all cfg condition
// as eval_condition can (and does) extra checks
.fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)),
Some(sym::all) => mis
.iter()
// We don't use all() here, because we want to evaluate all cfg condition
// as eval_condition can (and does) extra checks
.fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)),
Some(sym::not) => {
let [mi] = mis.as_slice() else {
dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span });
return false;
};
!eval_condition(mi, sess, features, eval)
}
Some(sym::target) => {
if let Some(features) = features
&& !features.cfg_target_compact()
{
feature_err(
sess,
sym::cfg_target_compact,
cfg.span,
fluent_generated::attr_parsing_unstable_cfg_target_compact,
)
.emit();
}
mis.iter().fold(true, |res, mi| {
let Some(mut mi) = mi.meta_item().cloned() else {
dcx.emit_err(session_diagnostics::CfgPredicateIdentifier {
span: mi.span(),
});
return false;
};
if let [seg, ..] = &mut mi.path.segments[..] {
seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
}
res & eval_condition(&MetaItemInner::MetaItem(mi), sess, features, eval)
})
}
_ => {
dcx.emit_err(session_diagnostics::InvalidPredicate {
span: cfg.span,
predicate: pprust::path_to_string(&cfg.path),
});
false
}
}
}
MetaItemKind::Word | MetaItemKind::NameValue(..)
if cfg.path.segments.len() != 1
|| cfg.path.segments[0].ident.is_path_segment_keyword() =>
{
dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
true
}
MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
span: lit.span,
reason: UnsupportedLiteralReason::CfgString,
is_bytestr: lit.kind.is_bytestr(),
start_point_span: sess.source_map().start_point(lit.span),
});
true
}
MetaItemKind::Word | MetaItemKind::NameValue(..) => {
let ident = cfg.ident().expect("multi-segment cfg predicate");
eval(Condition {
name: ident.name,
name_span: ident.span,
value: cfg.value_str(),
value_span: cfg.name_value_literal_span(),
span: cfg.span,
})
}
}
}

View file

@ -1,35 +1,22 @@
use rustc_ast::token::Token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AttrStyle, NodeId, token};
use rustc_data_structures::fx::FxHashMap;
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::AttrPath;
use rustc_hir::attrs::CfgEntry;
use rustc_hir::{AttrPath, Target};
use rustc_parse::exp;
use rustc_parse::parser::{Parser, Recovery};
use rustc_parse::parser::Parser;
use rustc_session::Session;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::UNREACHABLE_CFG_SELECT_PREDICATES;
use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
use rustc_span::{ErrorGuaranteed, Ident, Span};
use crate::parser::MetaItemOrLitParser;
use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry};
#[derive(Clone)]
pub enum CfgSelectPredicate {
Cfg(CfgEntry),
Wildcard(Token),
}
impl CfgSelectPredicate {
fn span(&self) -> Span {
match self {
CfgSelectPredicate::Cfg(cfg_entry) => cfg_entry.span(),
CfgSelectPredicate::Wildcard(token) => token.span,
}
}
}
#[derive(Default)]
pub struct CfgSelectBranches {
/// All the conditional branches.
@ -41,33 +28,6 @@ pub struct CfgSelectBranches {
pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>,
}
impl CfgSelectBranches {
/// Removes the top-most branch for which `predicate` returns `true`,
/// or the wildcard if none of the reachable branches satisfied the predicate.
pub fn pop_first_match<F>(&mut self, predicate: F) -> Option<(TokenStream, Span)>
where
F: Fn(&CfgEntry) -> bool,
{
for (index, (cfg, _, _)) in self.reachable.iter().enumerate() {
if predicate(cfg) {
let matched = self.reachable.remove(index);
return Some((matched.1, matched.2));
}
}
self.wildcard.take().map(|(_, tts, span)| (tts, span))
}
/// Consume this value and iterate over all the `TokenStream`s that it stores.
pub fn into_iter_tts(self) -> impl Iterator<Item = (TokenStream, Span)> {
let it1 = self.reachable.into_iter().map(|(_, tts, span)| (tts, span));
let it2 = self.wildcard.into_iter().map(|(_, tts, span)| (tts, span));
let it3 = self.unreachable.into_iter().map(|(_, tts, span)| (tts, span));
it1.chain(it2).chain(it3)
}
}
pub fn parse_cfg_select(
p: &mut Parser<'_>,
sess: &Session,
@ -91,10 +51,7 @@ pub fn parse_cfg_select(
}
}
} else {
let meta = MetaItemOrLitParser::parse_single(
p,
ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
)
let meta = MetaItemOrLitParser::parse_single(p, ShouldEmit::ErrorsAndLints)
.map_err(|diag| diag.emit())?;
let cfg_span = meta.span();
let cfg = AttributeParser::parse_single_args(
@ -102,15 +59,16 @@ pub fn parse_cfg_select(
cfg_span,
cfg_span,
AttrStyle::Inner,
AttrPath { segments: vec![sym::cfg_select].into_boxed_slice(), span: cfg_span },
AttrPath {
segments: vec![Ident::from_str("cfg_select")].into_boxed_slice(),
span: cfg_span,
},
None,
ParsedDescription::Macro,
cfg_span,
lint_node_id,
// Doesn't matter what the target actually is here.
Target::Crate,
features,
ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
ShouldEmit::ErrorsAndLints,
&meta,
parse_cfg_entry,
&AttributeTemplate::default(),
@ -128,102 +86,5 @@ pub fn parse_cfg_select(
}
}
if let Some(features) = features
&& features.enabled(sym::cfg_select)
{
let it = branches
.reachable
.iter()
.map(|(entry, _, _)| CfgSelectPredicate::Cfg(entry.clone()))
.chain(branches.wildcard.as_ref().map(|(t, _, _)| CfgSelectPredicate::Wildcard(*t)))
.chain(
branches.unreachable.iter().map(|(entry, _, _)| CfgSelectPredicate::clone(entry)),
);
lint_unreachable(p, it, lint_node_id);
}
Ok(branches)
}
fn lint_unreachable(
p: &mut Parser<'_>,
predicates: impl Iterator<Item = CfgSelectPredicate>,
lint_node_id: NodeId,
) {
// Symbols that have a known value.
let mut known = FxHashMap::<Symbol, bool>::default();
let mut wildcard_span = None;
let mut it = predicates;
let branch_is_unreachable = |predicate: CfgSelectPredicate, wildcard_span| {
let span = predicate.span();
p.psess.buffer_lint(
UNREACHABLE_CFG_SELECT_PREDICATES,
span,
lint_node_id,
BuiltinLintDiag::UnreachableCfg { span, wildcard_span },
);
};
for predicate in &mut it {
let CfgSelectPredicate::Cfg(ref cfg_entry) = predicate else {
wildcard_span = Some(predicate.span());
break;
};
match cfg_entry {
CfgEntry::Bool(true, _) => {
wildcard_span = Some(predicate.span());
break;
}
CfgEntry::Bool(false, _) => continue,
CfgEntry::NameValue { name, value, .. } => match value {
None => {
// `name` will be false in all subsequent branches.
let current = known.insert(*name, false);
match current {
None => continue,
Some(false) => {
branch_is_unreachable(predicate, None);
break;
}
Some(true) => {
// this branch will be taken, so all subsequent branches are unreachable.
break;
}
}
}
Some(_) => { /* for now we don't bother solving these */ }
},
CfgEntry::Not(inner, _) => match &**inner {
CfgEntry::NameValue { name, value: None, .. } => {
// `name` will be true in all subsequent branches.
let current = known.insert(*name, true);
match current {
None => continue,
Some(true) => {
branch_is_unreachable(predicate, None);
break;
}
Some(false) => {
// this branch will be taken, so all subsequent branches are unreachable.
break;
}
}
}
_ => { /* for now we don't bother solving these */ }
},
CfgEntry::All(_, _) | CfgEntry::Any(_, _) => {
/* for now we don't bother solving these */
}
CfgEntry::Version(..) => { /* don't bother solving these */ }
}
}
for predicate in it {
branch_is_unreachable(predicate, wildcard_span)
}
}

View file

@ -1,33 +0,0 @@
use super::prelude::*;
pub(crate) struct CfiEncodingParser;
impl<S: Stage> SingleAttributeParser<S> for CfiEncodingParser {
const PATH: &[Symbol] = &[sym::cfi_encoding];
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
Allow(Target::Struct),
Allow(Target::ForeignTy),
Allow(Target::Enum),
Allow(Target::Union),
]);
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "encoding");
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::cfi_encoding));
return None;
};
let Some(value_str) = name_value.value_as_str() else {
cx.expected_string_literal(name_value.value_span, None);
return None;
};
if value_str.as_str().trim().is_empty() {
cx.expected_non_empty_string_literal(name_value.value_span);
return None;
}
Some(AttributeKind::CfiEncoding { encoding: value_str })
}
}

View file

@ -23,9 +23,9 @@ impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
]);
const TEMPLATE: AttributeTemplate = template!(List: &["size", "speed", "none"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span, args);
cx.expected_list(cx.attr_span);
return None;
};
@ -57,6 +57,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for ColdParser {
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),
@ -83,7 +84,7 @@ impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
]);
const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
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, &[sym::on, sym::off]);
return None;
@ -134,7 +135,7 @@ impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
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;
@ -163,7 +164,7 @@ impl<S: Stage> SingleAttributeParser<S> for ObjcClassParser {
AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
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;
@ -181,7 +182,7 @@ impl<S: Stage> SingleAttributeParser<S> for ObjcClassParser {
cx.emit_err(NullOnObjcClass { span: nv.value_span });
return None;
}
Some(AttributeKind::RustcObjcClass { classname, span: cx.attr_span })
Some(AttributeKind::ObjcClass { classname, span: cx.attr_span })
}
}
@ -195,7 +196,7 @@ impl<S: Stage> SingleAttributeParser<S> for ObjcSelectorParser {
AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
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;
@ -213,7 +214,7 @@ impl<S: Stage> SingleAttributeParser<S> for ObjcSelectorParser {
cx.emit_err(NullOnObjcSelector { span: nv.value_span });
return None;
}
Some(AttributeKind::RustcObjcSelector { methname, span: cx.attr_span })
Some(AttributeKind::ObjcSelector { methname, span: cx.attr_span })
}
}
@ -342,7 +343,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for TrackCallerParser {
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Method(MethodKind::Trait { body: true })),
Allow(Target::Method(MethodKind::TraitImpl)),
Allow(Target::Method(MethodKind::Trait { body: false })), // `#[track_caller]` is inherited from trait methods
Allow(Target::Method(MethodKind::Trait { body: false })),
Allow(Target::ForeignFn),
Allow(Target::Closure),
Warn(Target::MacroDef),
@ -471,13 +472,13 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
}
}
fn parse_tf_attribute<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = (Symbol, Span)> {
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, args);
cx.expected_list(cx.attr_span);
return features;
};
if list.is_empty() {
@ -528,10 +529,10 @@ impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
};
const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c {
parse_tf_attribute(cx, args)
}
@ -566,10 +567,10 @@ impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
Allow(Target::Method(MethodKind::TraitImpl)),
]);
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c {
parse_tf_attribute(cx, args)
}
}
@ -598,9 +599,9 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span, args);
cx.expected_list(cx.attr_span);
return None;
};
@ -690,16 +691,6 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
}
}
pub(crate) struct ThreadLocalParser;
impl<S: Stage> NoArgsAttributeParser<S> for ThreadLocalParser {
const PATH: &[Symbol] = &[sym::thread_local];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal;
}
pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcPassIndirectlyInNonRusticAbisParser {
@ -708,109 +699,3 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcPassIndirectlyInNonRusticAbisPa
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
}
pub(crate) struct EiiForeignItemParser;
impl<S: Stage> NoArgsAttributeParser<S> for EiiForeignItemParser {
const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::EiiForeignItem;
}
pub(crate) struct PatchableFunctionEntryParser;
impl<S: Stage> SingleAttributeParser<S> for PatchableFunctionEntryParser {
const PATH: &[Symbol] = &[sym::patchable_function_entry];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(meta_item_list) = args.list() else {
cx.expected_list(cx.attr_span, args);
return None;
};
let mut prefix = None;
let mut entry = None;
if meta_item_list.len() == 0 {
cx.expected_list(meta_item_list.span, args);
return None;
}
let mut errored = false;
for item in meta_item_list.mixed() {
let Some(meta_item) = item.meta_item() else {
errored = true;
cx.expected_name_value(item.span(), None);
continue;
};
let Some(name_value_lit) = meta_item.args().name_value() else {
errored = true;
cx.expected_name_value(item.span(), None);
continue;
};
let attrib_to_write = match meta_item.ident().map(|ident| ident.name) {
Some(sym::prefix_nops) => {
// Duplicate prefixes are not allowed
if prefix.is_some() {
errored = true;
cx.duplicate_key(meta_item.path().span(), sym::prefix_nops);
continue;
}
&mut prefix
}
Some(sym::entry_nops) => {
// Duplicate entries are not allowed
if entry.is_some() {
errored = true;
cx.duplicate_key(meta_item.path().span(), sym::entry_nops);
continue;
}
&mut entry
}
_ => {
errored = true;
cx.expected_specific_argument(
meta_item.path().span(),
&[sym::prefix_nops, sym::entry_nops],
);
continue;
}
};
let rustc_ast::LitKind::Int(val, _) = name_value_lit.value_as_lit().kind else {
errored = true;
cx.expected_integer_literal(name_value_lit.value_span);
continue;
};
let Ok(val) = val.get().try_into() else {
errored = true;
cx.expected_integer_literal_in_range(
name_value_lit.value_span,
u8::MIN as isize,
u8::MAX as isize,
);
continue;
};
*attrib_to_write = Some(val);
}
if errored {
None
} else {
Some(AttributeKind::PatchableFunctionEntry {
prefix: prefix.unwrap_or(0),
entry: entry.unwrap_or(0),
})
}
}
}

View file

@ -13,7 +13,7 @@ impl<S: Stage> AttributeParser<S> for ConfusablesParser {
template!(List: &[r#""name1", "name2", ..."#]),
|this, cx, args| {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span, args);
cx.expected_list(cx.attr_span);
return;
};
@ -43,7 +43,7 @@ impl<S: Stage> AttributeParser<S> for ConfusablesParser {
return None;
}
Some(AttributeKind::RustcConfusables {
Some(AttributeKind::Confusables {
symbols: self.confusables,
first_span: self.first_span.unwrap(),
})

View file

@ -1,8 +1,4 @@
use rustc_hir::attrs::{CrateType, WindowsSubsystemKind};
use rustc_hir::lints::AttributeLintKind;
use rustc_session::lint::builtin::UNKNOWN_CRATE_TYPES;
use rustc_span::Symbol;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_hir::attrs::WindowsSubsystemKind;
use super::prelude::*;
@ -13,9 +9,9 @@ impl<S: Stage> SingleAttributeParser<S> for CrateNameParser {
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
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;
@ -30,56 +26,6 @@ impl<S: Stage> SingleAttributeParser<S> for CrateNameParser {
}
}
pub(crate) struct CrateTypeParser;
impl<S: Stage> CombineAttributeParser<S> for CrateTypeParser {
const PATH: &[Symbol] = &[sym::crate_type];
type Item = CrateType;
const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::CrateType(items);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const TEMPLATE: AttributeTemplate =
template!(NameValueStr: "crate type", "https://doc.rust-lang.org/reference/linkage.html");
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let ArgParser::NameValue(n) = args else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
let Some(crate_type) = n.value_as_str() else {
cx.expected_string_literal(n.value_span, Some(n.value_as_lit()));
return None;
};
let Ok(crate_type) = crate_type.try_into() else {
// We don't error on invalid `#![crate_type]` when not applied to a crate
if cx.shared.target == Target::Crate {
let candidate = find_best_match_for_name(
&CrateType::all_stable().iter().map(|(name, _)| *name).collect::<Vec<_>>(),
crate_type,
None,
);
cx.emit_lint(
UNKNOWN_CRATE_TYPES,
AttributeLintKind::CrateTypeUnknown {
span: n.value_span,
suggested: candidate,
},
n.value_span,
);
}
return None;
};
Some(crate_type)
}
}
pub(crate) struct RecursionLimitParser;
impl<S: Stage> SingleAttributeParser<S> for RecursionLimitParser {
@ -87,9 +33,9 @@ impl<S: Stage> SingleAttributeParser<S> for RecursionLimitParser {
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::AllowList(&[Allow(Target::Crate)]);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
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;
@ -110,9 +56,9 @@ impl<S: Stage> SingleAttributeParser<S> for MoveSizeLimitParser {
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
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;
@ -133,9 +79,9 @@ impl<S: Stage> SingleAttributeParser<S> for TypeLengthLimitParser {
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
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;
@ -156,9 +102,9 @@ impl<S: Stage> SingleAttributeParser<S> for PatternComplexityLimitParser {
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
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;
@ -177,7 +123,7 @@ 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::AllowList(&[Allow(Target::Crate)]);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoCore;
}
@ -186,25 +132,16 @@ 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::AllowList(&[Allow(Target::Crate)]);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoStd;
}
pub(crate) struct NoMainParser;
impl<S: Stage> NoArgsAttributeParser<S> for NoMainParser {
const PATH: &[Symbol] = &[sym::no_main];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoMain;
}
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::AllowList(&[Allow(Target::Crate)]);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcCoherenceIsCore;
}
@ -214,10 +151,10 @@ impl<S: Stage> SingleAttributeParser<S> for WindowsSubsystemParser {
const PATH: &[Symbol] = &[sym::windows_subsystem];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
const TEMPLATE: AttributeTemplate = template!(NameValueStr: ["windows", "console"], "https://doc.rust-lang.org/reference/runtime.html#the-windows_subsystem-attribute");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let Some(nv) = args.name_value() else {
cx.expected_name_value(
args.span().unwrap_or(cx.inner_span),
@ -238,66 +175,3 @@ impl<S: Stage> SingleAttributeParser<S> for WindowsSubsystemParser {
Some(AttributeKind::WindowsSubsystem(kind, cx.attr_span))
}
}
pub(crate) struct PanicRuntimeParser;
impl<S: Stage> NoArgsAttributeParser<S> for PanicRuntimeParser {
const PATH: &[Symbol] = &[sym::panic_runtime];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::PanicRuntime;
}
pub(crate) struct NeedsPanicRuntimeParser;
impl<S: Stage> NoArgsAttributeParser<S> for NeedsPanicRuntimeParser {
const PATH: &[Symbol] = &[sym::needs_panic_runtime];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NeedsPanicRuntime;
}
pub(crate) struct ProfilerRuntimeParser;
impl<S: Stage> NoArgsAttributeParser<S> for ProfilerRuntimeParser {
const PATH: &[Symbol] = &[sym::profiler_runtime];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ProfilerRuntime;
}
pub(crate) struct NoBuiltinsParser;
impl<S: Stage> NoArgsAttributeParser<S> for NoBuiltinsParser {
const PATH: &[Symbol] = &[sym::no_builtins];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoBuiltins;
}
pub(crate) struct RustcPreserveUbChecksParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcPreserveUbChecksParser {
const PATH: &[Symbol] = &[sym::rustc_preserve_ub_checks];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcPreserveUbChecks;
}
pub(crate) struct RustcNoImplicitBoundsParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcNoImplicitBoundsParser {
const PATH: &[Symbol] = &[sym::rustc_no_implicit_bounds];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoImplicitBounds;
}
pub(crate) struct DefaultLibAllocatorParser;
impl<S: Stage> NoArgsAttributeParser<S> for DefaultLibAllocatorParser {
const PATH: &[Symbol] = &[sym::default_lib_allocator];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::DefaultLibAllocator;
}

View file

@ -16,12 +16,12 @@ impl<S: Stage> CombineAttributeParser<S> for DebuggerViualizerParser {
type Item = DebugVisualizer;
const CONVERT: ConvertFn<Self::Item> = |v, _| AttributeKind::DebuggerVisualizer(v);
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
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(cx.attr_span, args);
cx.expected_list(args.span().unwrap_or(cx.attr_span));
return None;
};
let Some(single) = l.single() else {

View file

@ -1,5 +1,4 @@
use rustc_hir::attrs::{DeprecatedSince, Deprecation};
use rustc_hir::{RustcVersion, VERSION_PLACEHOLDER};
use super::prelude::*;
use super::util::parse_version;
@ -13,15 +12,15 @@ fn get<S: Stage>(
cx: &AcceptContext<'_, '_, S>,
name: Symbol,
param_span: Span,
arg: &ArgParser,
item: Option<Symbol>,
) -> Option<Ident> {
arg: &ArgParser<'_>,
item: &Option<Symbol>,
) -> Option<Symbol> {
if item.is_some() {
cx.duplicate_key(param_span, name);
return None;
}
if let Some(v) = arg.name_value() {
if let Some(value_str) = v.value_as_ident() {
if let Some(value_str) = v.value_as_str() {
Some(value_str)
} else {
cx.expected_string_literal(v.value_span, Some(&v.value_as_lit()));
@ -69,11 +68,11 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
NameValueStr: "reason"
);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let features = cx.features();
let mut since = None;
let mut note: Option<Ident> = None;
let mut note = None;
let mut suggestion = None;
let is_rustc = features.staged_api();
@ -93,16 +92,10 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
match ident_name {
Some(name @ sym::since) => {
since = Some(get(cx, name, param.span(), param.args(), since)?.name);
since = Some(get(cx, name, param.span(), param.args(), &since)?);
}
Some(name @ sym::note) => {
note = Some(get(
cx,
name,
param.span(),
param.args(),
note.map(|ident| ident.name),
)?);
note = Some(get(cx, name, param.span(), param.args(), &note)?);
}
Some(name @ sym::suggestion) => {
if !features.deprecated_suggestion() {
@ -114,15 +107,16 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
}
suggestion =
Some(get(cx, name, param.span(), param.args(), suggestion)?.name);
Some(get(cx, name, param.span(), param.args(), &suggestion)?);
}
_ => {
cx.expected_specific_argument(
cx.unknown_key(
param.span(),
param.path().to_string(),
if features.deprecated_suggestion() {
&[sym::since, sym::note, sym::suggestion]
&["since", "note", "suggestion"]
} else {
&[sym::since, sym::note]
&["since", "note"]
},
);
return None;
@ -131,7 +125,7 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
}
}
ArgParser::NameValue(v) => {
let Some(value) = v.value_as_ident() else {
let Some(value) = v.value_as_str() else {
cx.expected_string_literal(v.value_span, Some(v.value_as_lit()));
return None;
};
@ -144,8 +138,6 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
DeprecatedSince::Future
} else if !is_rustc {
DeprecatedSince::NonStandard(since)
} else if since.as_str() == VERSION_PLACEHOLDER {
DeprecatedSince::RustcVersion(RustcVersion::CURRENT)
} else if let Some(version) = parse_version(since) {
DeprecatedSince::RustcVersion(version)
} else {

View file

@ -1,31 +0,0 @@
use rustc_feature::{AttributeTemplate, template};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::AttributeLintKind;
use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES;
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 DoNotRecommendParser;
impl<S: Stage> SingleAttributeParser<S> for DoNotRecommendParser {
const PATH: &[Symbol] = &[sym::diagnostic, sym::do_not_recommend];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); // Checked in check_attr.
const TEMPLATE: AttributeTemplate = template!(Word /*doesn't matter */);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let attr_span = cx.attr_span;
if !matches!(args, ArgParser::NoArgs) {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::DoNotRecommendDoesNotExpectArgs,
attr_span,
);
}
Some(AttributeKind::DoNotRecommend { attr_span })
}
}

View file

@ -1,760 +0,0 @@
use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit};
use rustc_feature::template;
use rustc_hir::Target;
use rustc_hir::attrs::{
AttributeKind, CfgEntry, CfgHideShow, CfgInfo, DocAttribute, DocInline, HideOrShow,
};
use rustc_hir::lints::AttributeLintKind;
use rustc_span::{Span, Symbol, edition, sym};
use thin_vec::ThinVec;
use super::prelude::{ALL_TARGETS, AllowedTargets};
use super::{AcceptMapping, AttributeParser};
use crate::context::{AcceptContext, FinalizeContext, Stage};
use crate::parser::{ArgParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser};
use crate::session_diagnostics::{
DocAliasBadChar, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd, DocAttrNotCrateLevel,
DocAttributeNotAttribute, DocKeywordNotKeyword,
};
fn check_keyword<S: Stage>(cx: &mut AcceptContext<'_, '_, S>, keyword: Symbol, span: Span) -> bool {
// FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we
// can remove the `SelfTy` case here, remove `sym::SelfTy`, and update the
// `#[doc(keyword = "SelfTy")` attribute in `library/std/src/keyword_docs.rs`.
if keyword.is_reserved(|| edition::LATEST_STABLE_EDITION)
|| keyword.is_weak()
|| keyword == sym::SelfTy
{
return true;
}
cx.emit_err(DocKeywordNotKeyword { span, keyword });
false
}
fn check_attribute<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
attribute: Symbol,
span: Span,
) -> bool {
// FIXME: This should support attributes with namespace like `diagnostic::do_not_recommend`.
if rustc_feature::BUILTIN_ATTRIBUTE_MAP.contains_key(&attribute) {
return true;
}
cx.emit_err(DocAttributeNotAttribute { span, attribute });
false
}
/// Checks that an attribute is *not* used at the crate level. Returns `true` if valid.
fn check_attr_not_crate_level<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
span: Span,
attr_name: Symbol,
) -> bool {
if cx.shared.target == Target::Crate {
cx.emit_err(DocAttrNotCrateLevel { span, attr_name });
return false;
}
true
}
/// Checks that an attribute is used at the crate level. Returns `true` if valid.
fn check_attr_crate_level<S: Stage>(cx: &mut AcceptContext<'_, '_, S>, span: Span) -> bool {
if cx.shared.target != Target::Crate {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::AttrCrateLevelOnly,
span,
);
return false;
}
true
}
// FIXME: To be removed once merged and replace with `cx.expected_name_value(span, _name)`.
fn expected_name_value<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
span: Span,
_name: Option<Symbol>,
) {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::ExpectedNameValue,
span,
);
}
// FIXME: remove this method once merged and use `cx.expected_no_args(span)` instead.
fn expected_no_args<S: Stage>(cx: &mut AcceptContext<'_, '_, S>, span: Span) {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::ExpectedNoArgs,
span,
);
}
// FIXME: remove this method once merged and use `cx.expected_no_args(span)` instead.
// cx.expected_string_literal(span, _actual_literal);
fn expected_string_literal<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
span: Span,
_actual_literal: Option<&MetaItemLit>,
) {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::MalformedDoc,
span,
);
}
fn parse_keyword_and_attribute<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
path: &OwnedPathParser,
args: &ArgParser,
attr_value: &mut Option<(Symbol, Span)>,
attr_name: Symbol,
) {
let Some(nv) = args.name_value() else {
expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym());
return;
};
let Some(value) = nv.value_as_str() else {
expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit()));
return;
};
let ret = if attr_name == sym::keyword {
check_keyword(cx, value, nv.value_span)
} else {
check_attribute(cx, value, nv.value_span)
};
if !ret {
return;
}
let span = path.span();
if attr_value.is_some() {
cx.duplicate_key(span, path.word_sym().unwrap());
return;
}
if !check_attr_not_crate_level(cx, span, attr_name) {
return;
}
*attr_value = Some((value, span));
}
#[derive(Default, Debug)]
pub(crate) struct DocParser {
attribute: DocAttribute,
nb_doc_attrs: usize,
}
impl DocParser {
fn parse_single_test_doc_attr_item<S: Stage>(
&mut self,
cx: &mut AcceptContext<'_, '_, S>,
mip: &MetaItemParser,
) {
let path = mip.path();
let args = mip.args();
match path.word_sym() {
Some(sym::no_crate_inject) => {
if let Err(span) = args.no_args() {
expected_no_args(cx, span);
return;
}
if let Some(used_span) = self.attribute.no_crate_inject {
let unused_span = path.span();
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::UnusedDuplicate {
this: unused_span,
other: used_span,
warning: true,
},
unused_span,
);
return;
}
if !check_attr_crate_level(cx, path.span()) {
return;
}
self.attribute.no_crate_inject = Some(path.span())
}
Some(sym::attr) => {
let Some(list) = args.list() else {
// FIXME: remove this method once merged and uncomment the line below instead.
// cx.expected_list(cx.attr_span, args);
let span = cx.attr_span;
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::MalformedDoc,
span,
);
return;
};
// FIXME: convert list into a Vec of `AttributeKind` because current code is awful.
for attr in list.mixed() {
self.attribute.test_attrs.push(attr.span());
}
}
Some(name) => {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocTestUnknown { name },
path.span(),
);
}
None => {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocTestLiteral,
path.span(),
);
}
}
}
fn add_alias<S: Stage>(
&mut self,
cx: &mut AcceptContext<'_, '_, S>,
alias: Symbol,
span: Span,
) {
let attr_str = "`#[doc(alias = \"...\")]`";
if alias == sym::empty {
cx.emit_err(DocAliasEmpty { span, attr_str });
return;
}
let alias_str = alias.as_str();
if let Some(c) =
alias_str.chars().find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
{
cx.emit_err(DocAliasBadChar { span, attr_str, char_: c });
return;
}
if alias_str.starts_with(' ') || alias_str.ends_with(' ') {
cx.emit_err(DocAliasStartEnd { span, attr_str });
return;
}
if !check_attr_not_crate_level(cx, span, sym::alias) {
return;
}
if let Some(first_definition) = self.attribute.aliases.get(&alias).copied() {
cx.emit_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
AttributeLintKind::DuplicateDocAlias { first_definition },
span,
);
}
self.attribute.aliases.insert(alias, span);
}
fn parse_alias<S: Stage>(
&mut self,
cx: &mut AcceptContext<'_, '_, S>,
path: &OwnedPathParser,
args: &ArgParser,
) {
match args {
ArgParser::NoArgs => {
cx.emit_err(DocAliasMalformed { span: args.span().unwrap_or(path.span()) });
}
ArgParser::List(list) => {
for i in list.mixed() {
let Some(alias) = i.lit().and_then(|i| i.value_str()) else {
cx.expected_string_literal(i.span(), i.lit());
continue;
};
self.add_alias(cx, alias, i.span());
}
}
ArgParser::NameValue(nv) => {
let Some(alias) = nv.value_as_str() else {
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return;
};
self.add_alias(cx, alias, nv.value_span);
}
}
}
fn parse_inline<S: Stage>(
&mut self,
cx: &mut AcceptContext<'_, '_, S>,
path: &OwnedPathParser,
args: &ArgParser,
inline: DocInline,
) {
if let Err(span) = args.no_args() {
expected_no_args(cx, span);
return;
}
self.attribute.inline.push((inline, path.span()));
}
fn parse_cfg<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
// This function replaces cases like `cfg(all())` with `true`.
fn simplify_cfg(cfg_entry: &mut CfgEntry) {
match cfg_entry {
CfgEntry::All(cfgs, span) if cfgs.is_empty() => {
*cfg_entry = CfgEntry::Bool(true, *span)
}
CfgEntry::Any(cfgs, span) if cfgs.is_empty() => {
*cfg_entry = CfgEntry::Bool(false, *span)
}
CfgEntry::Not(cfg, _) => simplify_cfg(cfg),
_ => {}
}
}
if let Some(mut cfg_entry) = super::cfg::parse_cfg(cx, args) {
simplify_cfg(&mut cfg_entry);
self.attribute.cfg.push(cfg_entry);
}
}
fn parse_auto_cfg<S: Stage>(
&mut self,
cx: &mut AcceptContext<'_, '_, S>,
path: &OwnedPathParser,
args: &ArgParser,
) {
match args {
ArgParser::NoArgs => {
self.attribute.auto_cfg_change.push((true, path.span()));
}
ArgParser::List(list) => {
for meta in list.mixed() {
let MetaItemOrLitParser::MetaItemParser(item) = meta else {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocAutoCfgExpectsHideOrShow,
meta.span(),
);
continue;
};
let (kind, attr_name) = match item.path().word_sym() {
Some(sym::hide) => (HideOrShow::Hide, sym::hide),
Some(sym::show) => (HideOrShow::Show, sym::show),
_ => {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocAutoCfgExpectsHideOrShow,
item.span(),
);
continue;
}
};
let ArgParser::List(list) = item.args() else {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocAutoCfgHideShowExpectsList { attr_name },
item.span(),
);
continue;
};
let mut cfg_hide_show = CfgHideShow { kind, values: ThinVec::new() };
for item in list.mixed() {
let MetaItemOrLitParser::MetaItemParser(sub_item) = item else {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocAutoCfgHideShowUnexpectedItem { attr_name },
item.span(),
);
continue;
};
match sub_item.args() {
a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => {
let Some(name) = sub_item.path().word_sym() else {
// FIXME: remove this method once merged and uncomment the line
// below instead.
// cx.expected_identifier(sub_item.path().span());
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::MalformedDoc,
sub_item.path().span(),
);
continue;
};
if let Ok(CfgEntry::NameValue { name, value, .. }) =
super::cfg::parse_name_value(
name,
sub_item.path().span(),
a.name_value(),
sub_item.span(),
cx,
)
{
cfg_hide_show.values.push(CfgInfo {
name,
name_span: sub_item.path().span(),
// If `value` is `Some`, `a.name_value()` will always return
// `Some` as well.
value: value
.map(|v| (v, a.name_value().unwrap().value_span)),
})
}
}
_ => {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocAutoCfgHideShowUnexpectedItem {
attr_name,
},
sub_item.span(),
);
continue;
}
}
}
self.attribute.auto_cfg.push((cfg_hide_show, path.span()));
}
}
ArgParser::NameValue(nv) => {
let MetaItemLit { kind: LitKind::Bool(bool_value), span, .. } = nv.value_as_lit()
else {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocAutoCfgWrongLiteral,
nv.value_span,
);
return;
};
self.attribute.auto_cfg_change.push((*bool_value, *span));
}
}
}
fn parse_single_doc_attr_item<S: Stage>(
&mut self,
cx: &mut AcceptContext<'_, '_, S>,
mip: &MetaItemParser,
) {
let path = mip.path();
let args = mip.args();
macro_rules! no_args {
($ident: ident) => {{
if let Err(span) = args.no_args() {
expected_no_args(cx, span);
return;
}
// FIXME: It's errorring when the attribute is passed multiple times on the command
// line.
// The right fix for this would be to only check this rule if the attribute is
// not set on the command line but directly in the code.
// if self.attribute.$ident.is_some() {
// cx.duplicate_key(path.span(), path.word_sym().unwrap());
// return;
// }
self.attribute.$ident = Some(path.span());
}};
}
macro_rules! no_args_and_not_crate_level {
($ident: ident) => {{
if let Err(span) = args.no_args() {
expected_no_args(cx, span);
return;
}
let span = path.span();
if !check_attr_not_crate_level(cx, span, sym::$ident) {
return;
}
self.attribute.$ident = Some(span);
}};
}
macro_rules! no_args_and_crate_level {
($ident: ident) => {{
if let Err(span) = args.no_args() {
expected_no_args(cx, span);
return;
}
let span = path.span();
if !check_attr_crate_level(cx, span) {
return;
}
self.attribute.$ident = Some(span);
}};
}
macro_rules! string_arg_and_crate_level {
($ident: ident) => {{
let Some(nv) = args.name_value() else {
expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym());
return;
};
let Some(s) = nv.value_as_str() else {
expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit()));
return;
};
if !check_attr_crate_level(cx, path.span()) {
return;
}
// FIXME: It's errorring when the attribute is passed multiple times on the command
// line.
// The right fix for this would be to only check this rule if the attribute is
// not set on the command line but directly in the code.
// if self.attribute.$ident.is_some() {
// cx.duplicate_key(path.span(), path.word_sym().unwrap());
// return;
// }
self.attribute.$ident = Some((s, path.span()));
}};
}
match path.word_sym() {
Some(sym::alias) => self.parse_alias(cx, path, args),
Some(sym::hidden) => no_args!(hidden),
Some(sym::html_favicon_url) => string_arg_and_crate_level!(html_favicon_url),
Some(sym::html_logo_url) => string_arg_and_crate_level!(html_logo_url),
Some(sym::html_no_source) => no_args_and_crate_level!(html_no_source),
Some(sym::html_playground_url) => string_arg_and_crate_level!(html_playground_url),
Some(sym::html_root_url) => string_arg_and_crate_level!(html_root_url),
Some(sym::issue_tracker_base_url) => {
string_arg_and_crate_level!(issue_tracker_base_url)
}
Some(sym::inline) => self.parse_inline(cx, path, args, DocInline::Inline),
Some(sym::no_inline) => self.parse_inline(cx, path, args, DocInline::NoInline),
Some(sym::masked) => no_args!(masked),
Some(sym::cfg) => self.parse_cfg(cx, args),
Some(sym::notable_trait) => no_args!(notable_trait),
Some(sym::keyword) => parse_keyword_and_attribute(
cx,
path,
args,
&mut self.attribute.keyword,
sym::keyword,
),
Some(sym::attribute) => parse_keyword_and_attribute(
cx,
path,
args,
&mut self.attribute.attribute,
sym::attribute,
),
Some(sym::fake_variadic) => no_args_and_not_crate_level!(fake_variadic),
Some(sym::search_unbox) => no_args_and_not_crate_level!(search_unbox),
Some(sym::rust_logo) => no_args_and_crate_level!(rust_logo),
Some(sym::auto_cfg) => self.parse_auto_cfg(cx, path, args),
Some(sym::test) => {
let Some(list) = args.list() else {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocTestTakesList,
args.span().unwrap_or(path.span()),
);
return;
};
for i in list.mixed() {
match i {
MetaItemOrLitParser::MetaItemParser(mip) => {
self.parse_single_test_doc_attr_item(cx, mip);
}
MetaItemOrLitParser::Lit(lit) => {
// FIXME: remove this method once merged and uncomment the line
// below instead.
// cx.unexpected_literal(lit.span);
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::MalformedDoc,
lit.span,
);
}
}
}
}
Some(sym::spotlight) => {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocUnknownSpotlight { span: path.span() },
path.span(),
);
}
Some(sym::include) if let Some(nv) = args.name_value() => {
let inner = match cx.attr_style {
AttrStyle::Outer => "",
AttrStyle::Inner => "!",
};
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocUnknownInclude {
inner,
value: nv.value_as_lit().symbol,
span: path.span(),
},
path.span(),
);
}
Some(name @ (sym::passes | sym::no_default_passes)) => {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocUnknownPasses { name, span: path.span() },
path.span(),
);
}
Some(sym::plugins) => {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocUnknownPlugins { span: path.span() },
path.span(),
);
}
Some(name) => {
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocUnknownAny { name },
path.span(),
);
}
None => {
let full_name =
path.segments().map(|s| s.as_str()).intersperse("::").collect::<String>();
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::DocUnknownAny { name: Symbol::intern(&full_name) },
path.span(),
);
}
}
}
fn accept_single_doc_attr<S: Stage>(
&mut self,
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) {
match args {
ArgParser::NoArgs => {
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::IllFormedAttributeInput { suggestions, docs: None },
span,
);
}
ArgParser::List(items) => {
for i in items.mixed() {
match i {
MetaItemOrLitParser::MetaItemParser(mip) => {
self.nb_doc_attrs += 1;
self.parse_single_doc_attr_item(cx, mip);
}
MetaItemOrLitParser::Lit(lit) => {
expected_name_value(cx, lit.span, None);
}
}
}
}
ArgParser::NameValue(nv) => {
if nv.value_as_str().is_none() {
expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit()));
} else {
unreachable!(
"Should have been handled at the same time as sugar-syntaxed doc comments"
);
}
}
}
}
}
impl<S: Stage> AttributeParser<S> for DocParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
&[sym::doc],
template!(
List: &[
"alias",
"attribute",
"hidden",
"html_favicon_url",
"html_logo_url",
"html_no_source",
"html_playground_url",
"html_root_url",
"issue_tracker_base_url",
"inline",
"no_inline",
"masked",
"cfg",
"notable_trait",
"keyword",
"fake_variadic",
"search_unbox",
"rust_logo",
"auto_cfg",
"test",
"spotlight",
"include",
"no_default_passes",
"passes",
"plugins",
],
NameValueStr: "string"
),
|this, cx, args| {
this.accept_single_doc_attr(cx, args);
},
)];
// FIXME: Currently emitted from 2 different places, generating duplicated warnings.
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
// const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
// Allow(Target::ExternCrate),
// Allow(Target::Use),
// Allow(Target::Static),
// Allow(Target::Const),
// Allow(Target::Fn),
// Allow(Target::Mod),
// Allow(Target::ForeignMod),
// Allow(Target::TyAlias),
// Allow(Target::Enum),
// Allow(Target::Variant),
// Allow(Target::Struct),
// Allow(Target::Field),
// Allow(Target::Union),
// Allow(Target::Trait),
// Allow(Target::TraitAlias),
// Allow(Target::Impl { of_trait: true }),
// Allow(Target::Impl { of_trait: false }),
// Allow(Target::AssocConst),
// Allow(Target::Method(MethodKind::Inherent)),
// Allow(Target::Method(MethodKind::Trait { body: true })),
// Allow(Target::Method(MethodKind::Trait { body: false })),
// Allow(Target::Method(MethodKind::TraitImpl)),
// Allow(Target::AssocTy),
// Allow(Target::ForeignFn),
// Allow(Target::ForeignStatic),
// Allow(Target::ForeignTy),
// Allow(Target::MacroDef),
// Allow(Target::Crate),
// Error(Target::WherePredicate),
// ]);
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if self.nb_doc_attrs != 0 {
Some(AttributeKind::Doc(Box::new(self.attribute)))
} else {
None
}
}
}

View file

@ -15,7 +15,7 @@ impl<S: Stage> SingleAttributeParser<S> for DummyParser {
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
const TEMPLATE: AttributeTemplate = template!(Word); // Anything, really
fn convert(_: &mut AcceptContext<'_, '_, S>, _: &ArgParser) -> Option<AttributeKind> {
Some(AttributeKind::RustcDummy)
fn convert(_: &mut AcceptContext<'_, '_, S>, _: &ArgParser<'_>) -> Option<AttributeKind> {
Some(AttributeKind::Dummy)
}
}

View file

@ -3,14 +3,13 @@
// SingleAttributeParser which is what we have two of here.
use rustc_hir::attrs::{AttributeKind, InlineAttr};
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
use super::prelude::*;
pub(crate) struct InlineParser;
impl<S: Stage> SingleAttributeParser<S> for InlineParser {
const PATH: &[Symbol] = &[sym::inline];
const PATH: &'static [Symbol] = &[sym::inline];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
@ -34,7 +33,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
"https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute"
);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
match args {
ArgParser::NoArgs => Some(AttributeKind::Inline(InlineAttr::Hint, cx.attr_span)),
ArgParser::List(list) => {
@ -57,7 +56,9 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
}
}
ArgParser::NameValue(_) => {
cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;
}
}
@ -67,7 +68,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
pub(crate) struct RustcForceInlineParser;
impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
const PATH: &[Symbol] = &[sym::rustc_force_inline];
const PATH: &'static [Symbol] = &[sym::rustc_force_inline];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
@ -77,7 +78,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
const TEMPLATE: AttributeTemplate = template!(Word, List: &["reason"], NameValueStr: "reason");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let reason = match args {
ArgParser::NoArgs => None,
ArgParser::List(list) => {

View file

@ -1,73 +0,0 @@
use rustc_hir::attrs::InstructionSetAttr;
use super::prelude::*;
use crate::session_diagnostics;
pub(crate) struct InstructionSetParser;
impl<S: Stage> SingleAttributeParser<S> for InstructionSetParser {
const PATH: &[Symbol] = &[sym::instruction_set];
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
Allow(Target::Fn),
Allow(Target::Closure),
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Method(MethodKind::TraitImpl)),
Allow(Target::Method(MethodKind::Trait { body: true })),
]);
const TEMPLATE: AttributeTemplate = template!(List: &["set"], "https://doc.rust-lang.org/reference/attributes/codegen.html#the-instruction_set-attribute");
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
const POSSIBLE_SYMBOLS: &[Symbol] = &[sym::arm_a32, sym::arm_t32];
const POSSIBLE_ARM_SYMBOLS: &[Symbol] = &[sym::a32, sym::t32];
let Some(maybe_meta_item) = args.list().and_then(MetaItemListParser::single) else {
cx.expected_specific_argument(cx.attr_span, POSSIBLE_SYMBOLS);
return None;
};
let Some(meta_item) = maybe_meta_item.meta_item() else {
cx.expected_specific_argument(maybe_meta_item.span(), POSSIBLE_SYMBOLS);
return None;
};
let mut segments = meta_item.path().segments();
let Some(architecture) = segments.next() else {
cx.expected_specific_argument(meta_item.span(), POSSIBLE_SYMBOLS);
return None;
};
let Some(instruction_set) = segments.next() else {
cx.expected_specific_argument(architecture.span, POSSIBLE_SYMBOLS);
return None;
};
let instruction_set = match architecture.name {
sym::arm => {
if !cx.sess.target.has_thumb_interworking {
cx.dcx().emit_err(session_diagnostics::UnsupportedInstructionSet {
span: cx.attr_span,
instruction_set: sym::arm,
current_target: &cx.sess.opts.target_triple,
});
return None;
}
match instruction_set.name {
sym::a32 => InstructionSetAttr::ArmA32,
sym::t32 => InstructionSetAttr::ArmT32,
_ => {
cx.expected_specific_argument(instruction_set.span, POSSIBLE_ARM_SYMBOLS);
return None;
}
}
}
_ => {
cx.expected_specific_argument(architecture.span, POSSIBLE_SYMBOLS);
return None;
}
};
Some(AttributeKind::InstructionSet(instruction_set))
}
}

View file

@ -1,9 +1,7 @@
use rustc_errors::msg;
use rustc_feature::Features;
use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection};
use rustc_hir::attrs::*;
use rustc_session::Session;
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
use rustc_session::parse::feature_err;
use rustc_span::kw;
use rustc_target::spec::{Arch, BinaryFormat};
@ -11,11 +9,12 @@ use rustc_target::spec::{Arch, BinaryFormat};
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, ExportSymbolsNeedsStatic,
ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier,
LinkFrameworkApple, LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers,
NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, WholeArchiveNeedsStatic,
AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ImportNameTypeRaw, ImportNameTypeX86,
IncompatibleWasmLink, InvalidLinkModifier, LinkFrameworkApple, LinkOrdinalOutOfRange,
LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows,
WholeArchiveNeedsStatic,
};
pub(crate) struct LinkNameParser;
@ -33,7 +32,7 @@ impl<S: Stage> SingleAttributeParser<S> for LinkNameParser {
"https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_name-attribute"
);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
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;
@ -62,21 +61,23 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
], "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(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
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) => {
cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;
}
_ => {
cx.expected_list(cx.attr_span, args);
cx.expected_list(cx.attr_span);
return None;
}
};
@ -141,6 +142,8 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
macro report_unstable_modifier($feature: ident) {
if !features.$feature() {
// FIXME: make this translatable
#[expect(rustc::untranslatable_diagnostic)]
feature_err(
sess,
sym::$feature,
@ -165,14 +168,6 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
cx.emit_err(BundleNeedsStatic { span });
}
(sym::export_symbols, Some(NativeLibKind::Static { export_symbols, .. })) => {
assign_modifier(export_symbols)
}
(sym::export_symbols, _) => {
cx.emit_err(ExportSymbolsNeedsStatic { span });
}
(sym::verbatim, _) => assign_modifier(&mut verbatim),
(
@ -198,7 +193,6 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
span,
&[
sym::bundle,
sym::export_symbols,
sym::verbatim,
sym::whole_dash_archive,
sym::as_dash_needed,
@ -249,7 +243,7 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
impl LinkParser {
fn parse_link_name<S: Stage>(
item: &MetaItemParser,
item: &MetaItemParser<'_>,
name: &mut Option<(Symbol, Span)>,
cx: &mut AcceptContext<'_, '_, S>,
) -> bool {
@ -274,7 +268,7 @@ impl LinkParser {
}
fn parse_link_kind<S: Stage>(
item: &MetaItemParser,
item: &MetaItemParser<'_>,
kind: &mut Option<NativeLibKind>,
cx: &mut AcceptContext<'_, '_, S>,
sess: &Session,
@ -294,9 +288,7 @@ impl LinkParser {
};
let link_kind = match link_kind {
kw::Static => {
NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }
}
kw::Static => NativeLibKind::Static { bundle: None, whole_archive: None },
sym::dylib => NativeLibKind::Dylib { as_needed: None },
sym::framework => {
if !sess.target.is_like_darwin {
@ -316,7 +308,7 @@ impl LinkParser {
sess,
sym::raw_dylib_elf,
nv.value_span,
msg!("link kind `raw-dylib` is unstable on ELF platforms"),
fluent_generated::attr_parsing_raw_dylib_elf_unstable,
)
.emit();
} else {
@ -331,7 +323,7 @@ impl LinkParser {
sess,
sym::link_arg_attribute,
nv.value_span,
msg!("link kind `link-arg` is unstable"),
fluent_generated::attr_parsing_link_arg_unstable,
)
.emit();
}
@ -356,7 +348,7 @@ impl LinkParser {
}
fn parse_link_modifiers<S: Stage>(
item: &MetaItemParser,
item: &MetaItemParser<'_>,
modifiers: &mut Option<(Symbol, Span)>,
cx: &mut AcceptContext<'_, '_, S>,
) -> bool {
@ -377,7 +369,7 @@ impl LinkParser {
}
fn parse_link_cfg<S: Stage>(
item: &MetaItemParser,
item: &MetaItemParser<'_>,
cfg: &mut Option<CfgEntry>,
cx: &mut AcceptContext<'_, '_, S>,
sess: &Session,
@ -388,7 +380,7 @@ impl LinkParser {
return true;
}
let Some(link_cfg) = item.args().list() else {
cx.expected_list(item.span(), item.args());
cx.expected_list(item.span());
return true;
};
let Some(link_cfg) = link_cfg.single() else {
@ -396,14 +388,20 @@ impl LinkParser {
return true;
};
if !features.link_cfg() {
feature_err(sess, sym::link_cfg, item.span(), msg!("link cfg is unstable")).emit();
feature_err(
sess,
sym::link_cfg,
item.span(),
fluent_generated::attr_parsing_link_cfg_unstable,
)
.emit();
}
*cfg = parse_cfg_entry(cx, link_cfg).ok();
true
}
fn parse_link_wasm_import_module<S: Stage>(
item: &MetaItemParser,
item: &MetaItemParser<'_>,
wasm_import_module: &mut Option<(Symbol, Span)>,
cx: &mut AcceptContext<'_, '_, S>,
) -> bool {
@ -424,7 +422,7 @@ impl LinkParser {
}
fn parse_link_import_name_type<S: Stage>(
item: &MetaItemParser,
item: &MetaItemParser<'_>,
import_name_type: &mut Option<(PeImportNameType, Span)>,
cx: &mut AcceptContext<'_, '_, S>,
) -> bool {
@ -472,6 +470,7 @@ impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser {
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)),
]);
@ -480,7 +479,7 @@ impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser {
"https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute"
);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
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;
@ -534,7 +533,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for StdInternalSymbolParser {
Allow(Target::Static),
Allow(Target::ForeignStatic),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcStdInternalSymbol;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::StdInternalSymbol;
}
pub(crate) struct LinkOrdinalParser;
@ -553,7 +552,7 @@ impl<S: Stage> SingleAttributeParser<S> for LinkOrdinalParser {
"https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute"
);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let ordinal = parse_single_integer(cx, args)?;
// According to the table at
@ -589,12 +588,12 @@ impl<S: Stage> SingleAttributeParser<S> for LinkageParser {
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),
Warn(Target::Method(MethodKind::Trait { body: false })), // Not inherited
]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: [
@ -609,7 +608,7 @@ impl<S: Stage> SingleAttributeParser<S> for LinkageParser {
"weak_odr",
]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
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;
@ -661,21 +660,3 @@ impl<S: Stage> SingleAttributeParser<S> for LinkageParser {
Some(AttributeKind::Linkage(linkage, cx.attr_span))
}
}
pub(crate) struct NeedsAllocatorParser;
impl<S: Stage> NoArgsAttributeParser<S> for NeedsAllocatorParser {
const PATH: &[Symbol] = &[sym::needs_allocator];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NeedsAllocator;
}
pub(crate) struct CompilerBuiltinsParser;
impl<S: Stage> NoArgsAttributeParser<S> for CompilerBuiltinsParser {
const PATH: &[Symbol] = &[sym::compiler_builtins];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CompilerBuiltins;
}

View file

@ -11,7 +11,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for AsPtrParser {
Allow(Target::Method(MethodKind::Trait { body: true })),
Allow(Target::Method(MethodKind::TraitImpl)),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcAsPtr;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::AsPtr;
}
pub(crate) struct PubTransparentParser;
@ -23,7 +23,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for PubTransparentParser {
Allow(Target::Enum),
Allow(Target::Union),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPubTransparent;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::PubTransparent;
}
pub(crate) struct PassByValueParser;
@ -35,7 +35,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for PassByValueParser {
Allow(Target::Enum),
Allow(Target::TyAlias),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassByValue;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::PassByValue;
}
pub(crate) struct RustcShouldNotBeCalledOnConstItems;

View file

@ -1,7 +1,8 @@
use rustc_hir::attrs::{CollapseMacroDebuginfo, MacroUseArgs};
use rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS;
use rustc_errors::DiagArgValue;
use rustc_hir::attrs::MacroUseArgs;
use super::prelude::*;
use crate::session_diagnostics::IllFormedAttributeInputLint;
pub(crate) struct MacroEscapeParser;
impl<S: Stage> NoArgsAttributeParser<S> for MacroEscapeParser {
@ -99,8 +100,15 @@ impl<S: Stage> AttributeParser<S> for MacroUseParser {
}
}
}
ArgParser::NameValue(nv) => {
cx.expected_list_or_no_args(nv.args_span());
ArgParser::NameValue(_) => {
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,
});
}
}
},
@ -139,79 +147,45 @@ impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
Error(Target::Crate),
]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
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 {
cx.warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS);
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,
_ => {
cx.warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS);
let span = cx.attr_span;
let suggestions = cx.suggestions();
cx.emit_lint(
AttributeLintKind::InvalidMacroExportArguments { suggestions },
span,
);
return None;
}
}
}
ArgParser::NameValue(nv) => {
cx.expected_list_or_no_args(nv.args_span());
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 })
}
}
pub(crate) struct CollapseDebugInfoParser;
impl<S: Stage> SingleAttributeParser<S> for CollapseDebugInfoParser {
const PATH: &[Symbol] = &[sym::collapse_debuginfo];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const TEMPLATE: AttributeTemplate = template!(
List: &["no", "external", "yes"],
"https://doc.rust-lang.org/reference/attributes/debugger.html#the-collapse_debuginfo-attribute"
);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::MacroDef)]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span, args);
return None;
};
let Some(single) = list.single() else {
cx.expected_single_argument(list.span);
return None;
};
let Some(mi) = single.meta_item() else {
cx.unexpected_literal(single.span());
return None;
};
if let Err(err) = mi.args().no_args() {
cx.expected_no_args(err);
}
let path = mi.path().word_sym();
let info = match path {
Some(sym::yes) => CollapseMacroDebuginfo::Yes,
Some(sym::no) => CollapseMacroDebuginfo::No,
Some(sym::external) => CollapseMacroDebuginfo::External,
_ => {
cx.expected_specific_argument(mi.span(), &[sym::yes, sym::no, sym::external]);
return None;
}
};
Some(AttributeKind::CollapseDebugInfo(info))
}
}
pub(crate) struct RustcProcMacroDeclsParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcProcMacroDeclsParser {
const PATH: &[Symbol] = &[sym::rustc_proc_macro_decls];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Static)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcProcMacroDecls;
}

View file

@ -32,34 +32,27 @@ mod prelude;
pub(crate) mod allow_unstable;
pub(crate) mod body;
pub(crate) mod cfg;
pub(crate) mod cfg_old;
pub(crate) mod cfg_select;
pub(crate) mod cfi_encoding;
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 do_not_recommend;
pub(crate) mod doc;
pub(crate) mod dummy;
pub(crate) mod inline;
pub(crate) mod instruction_set;
pub(crate) mod link_attrs;
pub(crate) mod lint_helpers;
pub(crate) mod loop_match;
pub(crate) mod macro_attrs;
pub(crate) mod must_not_suspend;
pub(crate) mod must_use;
pub(crate) mod no_implicit_prelude;
pub(crate) mod no_link;
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_allocator;
pub(crate) mod rustc_dump;
pub(crate) mod rustc_internal;
pub(crate) mod semantics;
pub(crate) mod stability;
@ -68,7 +61,7 @@ pub(crate) mod traits;
pub(crate) mod transparency;
pub(crate) mod util;
type AcceptFn<T, S> = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser);
type AcceptFn<T, S> = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser<'_>);
type AcceptMapping<T, S> = &'static [(&'static [Symbol], AttributeTemplate, AcceptFn<T, S>)];
/// An [`AttributeParser`] is a type which searches for syntactic attributes.
@ -139,7 +132,7 @@ pub(crate) trait SingleAttributeParser<S: Stage>: 'static {
const TEMPLATE: AttributeTemplate;
/// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`]
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind>;
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind>;
}
/// Use in combination with [`SingleAttributeParser`].
@ -288,7 +281,7 @@ impl<T: NoArgsAttributeParser<S>, S: Stage> SingleAttributeParser<S> for Without
const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS;
const TEMPLATE: AttributeTemplate = template!(Word);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
if let Err(span) = args.no_args() {
cx.expected_no_args(span);
}
@ -321,10 +314,10 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
const TEMPLATE: AttributeTemplate;
/// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`]
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item>;
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c;
}
/// Use in combination with [`CombineAttributeParser`].

View file

@ -1,35 +0,0 @@
use super::prelude::*;
pub(crate) struct MustNotSuspendParser;
impl<S: Stage> SingleAttributeParser<S> for MustNotSuspendParser {
const PATH: &[rustc_span::Symbol] = &[sym::must_not_suspend];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Struct),
Allow(Target::Enum),
Allow(Target::Union),
Allow(Target::Trait),
]);
const TEMPLATE: AttributeTemplate = template!(Word, List: &["count"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let reason = match args {
ArgParser::NameValue(reason) => match reason.value_as_str() {
Some(val) => Some(val),
None => {
cx.expected_nv_or_no_args(reason.value_span);
return None;
}
},
ArgParser::NoArgs => None,
ArgParser::List(list) => {
cx.expected_nv_or_no_args(list.span);
return None;
}
};
Some(AttributeKind::MustNotSupend { reason })
}
}

View file

@ -1,4 +1,7 @@
use rustc_errors::DiagArgValue;
use super::prelude::*;
use crate::session_diagnostics::IllFormedAttributeInputLint;
pub(crate) struct MustUseParser;
@ -26,7 +29,7 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
"https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"
);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
Some(AttributeKind::MustUse {
span: cx.attr_span,
reason: match args {
@ -41,8 +44,15 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
};
Some(value_str)
}
ArgParser::List(list) => {
cx.expected_nv_or_no_args(list.span);
ArgParser::List(_) => {
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: cx.attr_span,
});
return None;
}
},

View file

@ -1,14 +0,0 @@
use super::prelude::*;
pub(crate) struct NoLinkParser;
impl<S: Stage> NoArgsAttributeParser<S> for NoLinkParser {
const PATH: &[Symbol] = &[sym::no_link];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::ExternCrate),
Warn(Target::Field),
Warn(Target::Arm),
Warn(Target::MacroDef),
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoLink;
}

View file

@ -13,7 +13,7 @@ impl<S: Stage> SingleAttributeParser<S> for PathParser {
"https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute"
);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
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;

View file

@ -4,6 +4,8 @@ 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};

View file

@ -1,6 +1,3 @@
use rustc_hir::lints::AttributeLintKind;
use rustc_session::lint::builtin::AMBIGUOUS_DERIVE_HELPERS;
use super::prelude::*;
const PROC_MACRO_ALLOWED_TARGETS: AllowedTargets =
@ -33,7 +30,7 @@ impl<S: Stage> SingleAttributeParser<S> for ProcMacroDeriveParser {
"https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros"
);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let (trait_name, helper_attrs) = parse_derive_like(cx, args, true)?;
Some(AttributeKind::ProcMacroDerive {
trait_name: trait_name.expect("Trait name is mandatory, so it is present"),
@ -52,7 +49,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcBuiltinMacroParser {
const TEMPLATE: AttributeTemplate =
template!(List: &["TraitName", "TraitName, attributes(name1, name2, ...)"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let (builtin_name, helper_attrs) = parse_derive_like(cx, args, false)?;
Some(AttributeKind::RustcBuiltinMacro { builtin_name, helper_attrs, span: cx.attr_span })
}
@ -60,7 +57,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcBuiltinMacroParser {
fn parse_derive_like<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
args: &ArgParser<'_>,
trait_name_mandatory: bool,
) -> Option<(Option<Symbol>, ThinVec<Symbol>)> {
let Some(list) = args.list() else {
@ -68,7 +65,7 @@ fn parse_derive_like<S: Stage>(
if args.no_args().is_ok() && !trait_name_mandatory {
return Some((None, ThinVec::new()));
}
cx.expected_list(cx.attr_span, args);
cx.expected_list(cx.attr_span);
return None;
};
let mut items = list.mixed();
@ -99,7 +96,7 @@ fn parse_derive_like<S: Stage>(
let mut attributes = ThinVec::new();
if let Some(attrs) = items.next() {
let Some(attr_list) = attrs.meta_item() else {
cx.unexpected_literal(attrs.span());
cx.expected_list(attrs.span());
return None;
};
if !attr_list.path().word_is(sym::attributes) {
@ -107,7 +104,7 @@ fn parse_derive_like<S: Stage>(
return None;
}
let Some(attr_list) = attr_list.args().list() else {
cx.expected_list(attrs.span(), attr_list.args());
cx.expected_list(attrs.span());
return None;
};
@ -129,13 +126,6 @@ fn parse_derive_like<S: Stage>(
cx.expected_identifier(ident.span);
return None;
}
if rustc_feature::is_builtin_attr_name(ident.name) {
cx.emit_lint(
AMBIGUOUS_DERIVE_HELPERS,
AttributeLintKind::AmbiguousDeriveHelpers,
ident.span,
);
}
attributes.push(ident.name);
}
}

View file

@ -25,9 +25,9 @@ impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
const TEMPLATE: AttributeTemplate = template!(List: &[r#"dialect = "...", phase = "...""#]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span, args);
cx.expected_list(cx.attr_span);
return None;
};
@ -46,8 +46,9 @@ impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
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(..) = meta_item.path().word() {
cx.expected_specific_argument(meta_item.span(), &[sym::dialect, sym::phase]);
} 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);
@ -69,7 +70,7 @@ impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
fn extract_value<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
key: Symbol,
arg: &ArgParser,
arg: &ArgParser<'_>,
span: Span,
out_val: &mut Option<(Symbol, Span)>,
failed: &mut bool,

View file

@ -26,14 +26,14 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
"https://doc.rust-lang.org/reference/type-layout.html#representations"
);
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c {
let mut reprs = Vec::new();
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span, args);
cx.expected_list(cx.attr_span);
return reprs;
};
@ -98,7 +98,10 @@ fn int_type_of_word(s: Symbol) -> Option<IntType> {
}
}
fn parse_repr<S: Stage>(cx: &AcceptContext<'_, '_, S>, param: &MetaItemParser) -> Option<ReprAttr> {
fn parse_repr<S: Stage>(
cx: &AcceptContext<'_, '_, S>,
param: &MetaItemParser<'_>,
) -> Option<ReprAttr> {
use ReprAttr::*;
// FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
@ -189,7 +192,7 @@ enum AlignKind {
fn parse_repr_align<S: Stage>(
cx: &AcceptContext<'_, '_, S>,
list: &MetaItemListParser,
list: &MetaItemListParser<'_>,
param_span: Span,
align_kind: AlignKind,
) -> Option<ReprAttr> {
@ -272,13 +275,17 @@ fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
pub(crate) struct AlignParser(Option<(Align, Span)>);
impl AlignParser {
const PATH: &[Symbol] = &[sym::rustc_align];
const PATH: &'static [Symbol] = &[sym::rustc_align];
const TEMPLATE: AttributeTemplate = template!(List: &["<alignment in bytes>"]);
fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
fn parse<'c, S: Stage>(
&mut self,
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) {
match args {
ArgParser::NoArgs | ArgParser::NameValue(_) => {
cx.expected_list(cx.attr_span, args);
cx.expected_list(cx.attr_span);
}
ArgParser::List(list) => {
let Some(align) = list.single() else {
@ -315,7 +322,7 @@ impl<S: Stage> AttributeParser<S> for AlignParser {
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Method(MethodKind::Trait { body: true })),
Allow(Target::Method(MethodKind::TraitImpl)),
Allow(Target::Method(MethodKind::Trait { body: false })), // `#[align]` is inherited from trait methods
Allow(Target::Method(MethodKind::Trait { body: false })),
Allow(Target::ForeignFn),
]);
@ -329,10 +336,14 @@ impl<S: Stage> AttributeParser<S> for AlignParser {
pub(crate) struct AlignStaticParser(AlignParser);
impl AlignStaticParser {
const PATH: &[Symbol] = &[sym::rustc_align_static];
const PATH: &'static [Symbol] = &[sym::rustc_align_static];
const TEMPLATE: AttributeTemplate = AlignParser::TEMPLATE;
fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
fn parse<'c, S: Stage>(
&mut self,
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) {
self.0.parse(cx, args)
}
}

View file

@ -1,60 +0,0 @@
use super::prelude::*;
pub(crate) struct RustcAllocatorParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcAllocatorParser {
const PATH: &[Symbol] = &[sym::rustc_allocator];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcAllocator;
}
pub(crate) struct RustcAllocatorZeroedParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcAllocatorZeroedParser {
const PATH: &[Symbol] = &[sym::rustc_allocator_zeroed];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcAllocatorZeroed;
}
pub(crate) struct RustcAllocatorZeroedVariantParser;
impl<S: Stage> SingleAttributeParser<S> for RustcAllocatorZeroedVariantParser {
const PATH: &[Symbol] = &[sym::rustc_allocator_zeroed_variant];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "function");
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(name) = args.name_value().and_then(NameValueParser::value_as_str) else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
Some(AttributeKind::RustcAllocatorZeroedVariant { name })
}
}
pub(crate) struct RustcDeallocatorParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcDeallocatorParser {
const PATH: &[Symbol] = &[sym::rustc_deallocator];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDeallocator;
}
pub(crate) struct RustcReallocatorParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcReallocatorParser {
const PATH: &[Symbol] = &[sym::rustc_reallocator];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcReallocator;
}

View file

@ -1,62 +0,0 @@
use rustc_hir::Target;
use rustc_hir::attrs::AttributeKind;
use rustc_span::{Span, Symbol, sym};
use crate::attributes::prelude::Allow;
use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
use crate::context::Stage;
use crate::target_checking::AllowedTargets;
pub(crate) struct RustcDumpUserArgsParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpUserArgsParser {
const PATH: &[Symbol] = &[sym::rustc_dump_user_args];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpUserArgs;
}
pub(crate) struct RustcDumpDefParentsParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpDefParentsParser {
const PATH: &[Symbol] = &[sym::rustc_dump_def_parents];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpDefParents;
}
pub(crate) struct RustcDumpItemBoundsParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpItemBoundsParser {
const PATH: &[Symbol] = &[sym::rustc_dump_item_bounds];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::AssocTy)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpItemBounds;
}
pub(crate) struct RustcDumpPredicatesParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpPredicatesParser {
const PATH: &[Symbol] = &[sym::rustc_dump_predicates];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Struct),
Allow(Target::Enum),
Allow(Target::Union),
Allow(Target::Trait),
Allow(Target::AssocTy),
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpPredicates;
}
pub(crate) struct RustcDumpVtableParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpVtableParser {
const PATH: &[Symbol] = &[sym::rustc_dump_vtable];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Impl { of_trait: true }),
Allow(Target::TyAlias),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcDumpVtable;
}

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,6 @@
use std::num::NonZero;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::target::GenericParamKind;
use rustc_hir::{
DefaultBodyStability, MethodKind, PartialConstStability, Stability, StabilityLevel,
StableSince, Target, UnstableReason, VERSION_PLACEHOLDER,
@ -9,7 +8,7 @@ use rustc_hir::{
use super::prelude::*;
use super::util::parse_version;
use crate::session_diagnostics::{self};
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
macro_rules! reject_outside_std {
($cx: ident) => {
@ -44,7 +43,7 @@ const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::TyAlias),
Allow(Target::Variant),
Allow(Target::Field),
Allow(Target::GenericParam { kind: GenericParamKind::Type, has_default: true }),
Allow(Target::Param),
Allow(Target::Static),
Allow(Target::ForeignFn),
Allow(Target::ForeignStatic),
@ -173,7 +172,7 @@ impl<S: Stage> AttributeParser<S> for BodyStabilityParser {
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
let (stability, span) = self.stability?;
Some(AttributeKind::RustcBodyStability { stability, span })
Some(AttributeKind::BodyStability { stability, span })
}
}
@ -185,7 +184,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for ConstStabilityIndirectParser {
Allow(Target::Fn),
Allow(Target::Method(MethodKind::Inherent)),
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcConstStabilityIndirect;
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ConstStabilityIndirect;
}
#[derive(Default)]
@ -244,20 +243,7 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
this.promotable = true;
}),
];
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Fn),
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Method(MethodKind::TraitImpl)),
Allow(Target::Method(MethodKind::Trait { body: true })),
Allow(Target::Impl { of_trait: false }),
Allow(Target::Impl { of_trait: true }),
Allow(Target::Use), // FIXME I don't think this does anything?
Allow(Target::Const),
Allow(Target::AssocConst),
Allow(Target::Trait),
Allow(Target::Static),
Allow(Target::Crate),
]);
const ALLOWED_TARGETS: AllowedTargets = ALLOWED_TARGETS;
fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if self.promotable {
@ -271,7 +257,7 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
let (stability, span) = self.stability?;
Some(AttributeKind::RustcConstStability { stability, span })
Some(AttributeKind::ConstStability { stability, span })
}
}
@ -281,7 +267,7 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
/// `name = value`
fn insert_value_into_option_or_error<S: Stage>(
cx: &AcceptContext<'_, '_, S>,
param: &MetaItemParser,
param: &MetaItemParser<'_>,
item: &mut Option<Symbol>,
name: Ident,
) -> Option<()> {
@ -303,20 +289,25 @@ fn insert_value_into_option_or_error<S: Stage>(
/// its stability information.
pub(crate) fn parse_stability<S: Stage>(
cx: &AcceptContext<'_, '_, S>,
args: &ArgParser,
args: &ArgParser<'_>,
) -> Option<(Symbol, StabilityLevel)> {
let mut feature = None;
let mut since = None;
let ArgParser::List(list) = args else {
cx.expected_list(cx.attr_span, args);
cx.expected_list(cx.attr_span);
return None;
};
for param in list.mixed() {
let param_span = param.span();
let Some(param) = param.meta_item() else {
cx.unexpected_literal(param.span());
cx.emit_err(session_diagnostics::UnsupportedLiteral {
span: param_span,
reason: UnsupportedLiteralReason::Generic,
is_bytestr: false,
start_point_span: cx.sess().source_map().start_point(param_span),
});
return None;
};
@ -329,7 +320,11 @@ pub(crate) fn parse_stability<S: Stage>(
insert_value_into_option_or_error(cx, &param, &mut since, word.unwrap())?
}
_ => {
cx.expected_specific_argument(param_span, &[sym::feature, sym::since]);
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param_span,
item: param.path().to_string(),
expected: &["feature", "since"],
});
return None;
}
}
@ -370,7 +365,7 @@ pub(crate) fn parse_stability<S: Stage>(
/// attribute, and return the feature name and its stability information.
pub(crate) fn parse_unstability<S: Stage>(
cx: &AcceptContext<'_, '_, S>,
args: &ArgParser,
args: &ArgParser<'_>,
) -> Option<(Symbol, StabilityLevel)> {
let mut feature = None;
let mut reason = None;
@ -381,13 +376,18 @@ pub(crate) fn parse_unstability<S: Stage>(
let mut old_name = None;
let ArgParser::List(list) = args else {
cx.expected_list(cx.attr_span, args);
cx.expected_list(cx.attr_span);
return None;
};
for param in list.mixed() {
let Some(param) = param.meta_item() else {
cx.unexpected_literal(param.span());
cx.emit_err(session_diagnostics::UnsupportedLiteral {
span: param.span(),
reason: UnsupportedLiteralReason::Generic,
is_bytestr: false,
start_point_span: cx.sess().source_map().start_point(param.span()),
});
return None;
};
@ -436,17 +436,11 @@ pub(crate) fn parse_unstability<S: Stage>(
insert_value_into_option_or_error(cx, &param, &mut old_name, word.unwrap())?
}
_ => {
cx.expected_specific_argument(
param.span(),
&[
sym::feature,
sym::reason,
sym::issue,
sym::soft,
sym::implied_by,
sym::old_name,
],
);
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param.span(),
item: param.path().to_string(),
expected: &["feature", "reason", "issue", "soft", "implied_by", "old_name"],
});
return None;
}
}

View file

@ -1,6 +1,3 @@
use rustc_hir::attrs::RustcAbiAttrKind;
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
use super::prelude::*;
pub(crate) struct IgnoreParser;
@ -16,20 +13,27 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
"https://doc.rust-lang.org/reference/attributes/testing.html#the-ignore-attribute"
);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
Some(AttributeKind::Ignore {
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.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(
AttributeLintKind::IllFormedAttributeInput { suggestions },
span,
);
return None;
};
Some(str_value)
}
ArgParser::List(_) => {
cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
return None;
}
},
@ -50,7 +54,7 @@ impl<S: Stage> SingleAttributeParser<S> for ShouldPanicParser {
"https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute"
);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
Some(AttributeKind::ShouldPanic {
span: cx.attr_span,
reason: match args {
@ -92,201 +96,3 @@ impl<S: Stage> SingleAttributeParser<S> for ShouldPanicParser {
})
}
}
pub(crate) struct RustcVarianceParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcVarianceParser {
const PATH: &[Symbol] = &[sym::rustc_variance];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Struct),
Allow(Target::Enum),
Allow(Target::Union),
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcVariance;
}
pub(crate) struct RustcVarianceOfOpaquesParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcVarianceOfOpaquesParser {
const PATH: &[Symbol] = &[sym::rustc_variance_of_opaques];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcVarianceOfOpaques;
}
pub(crate) struct ReexportTestHarnessMainParser;
impl<S: Stage> SingleAttributeParser<S> for ReexportTestHarnessMainParser {
const PATH: &[Symbol] = &[sym::reexport_test_harness_main];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(nv) = args.name_value() else {
cx.expected_name_value(
args.span().unwrap_or(cx.inner_span),
Some(sym::reexport_test_harness_main),
);
return None;
};
let Some(name) = nv.value_as_str() else {
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return None;
};
Some(AttributeKind::ReexportTestHarnessMain(name))
}
}
pub(crate) struct RustcAbiParser;
impl<S: Stage> SingleAttributeParser<S> for RustcAbiParser {
const PATH: &[Symbol] = &[sym::rustc_abi];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::debug, sym::assert_eq]);
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::TyAlias),
Allow(Target::Fn),
Allow(Target::ForeignFn),
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Method(MethodKind::Trait { body: true })),
Allow(Target::Method(MethodKind::Trait { body: false })),
Allow(Target::Method(MethodKind::TraitImpl)),
]);
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, &[sym::assert_eq, sym::debug]);
return None;
};
let Some(arg) = args.single() else {
cx.expected_single_argument(cx.attr_span);
return None;
};
let fail_incorrect_argument =
|span| cx.expected_specific_argument(span, &[sym::assert_eq, sym::debug]);
let Some(arg) = arg.meta_item() else {
fail_incorrect_argument(args.span);
return None;
};
let kind: RustcAbiAttrKind = match arg.path().word_sym() {
Some(sym::assert_eq) => RustcAbiAttrKind::AssertEq,
Some(sym::debug) => RustcAbiAttrKind::Debug,
None | Some(_) => {
fail_incorrect_argument(arg.span());
return None;
}
};
Some(AttributeKind::RustcAbi { attr_span: cx.attr_span, kind })
}
}
pub(crate) struct RustcDelayedBugFromInsideQueryParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcDelayedBugFromInsideQueryParser {
const PATH: &[Symbol] = &[sym::rustc_delayed_bug_from_inside_query];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDelayedBugFromInsideQuery;
}
pub(crate) struct RustcEvaluateWhereClausesParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcEvaluateWhereClausesParser {
const PATH: &[Symbol] = &[sym::rustc_evaluate_where_clauses];
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 })),
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEvaluateWhereClauses;
}
pub(crate) struct RustcOutlivesParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcOutlivesParser {
const PATH: &[Symbol] = &[sym::rustc_outlives];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Struct),
Allow(Target::Enum),
Allow(Target::Union),
Allow(Target::TyAlias),
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcOutlives;
}
pub(crate) struct TestRunnerParser;
impl<S: Stage> SingleAttributeParser<S> for TestRunnerParser {
const PATH: &[Symbol] = &[sym::test_runner];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const TEMPLATE: AttributeTemplate = template!(List: &["path"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span, args);
return None;
};
let Some(single) = list.single() else {
cx.expected_single_argument(list.span);
return None;
};
let Some(meta) = single.meta_item() else {
cx.unexpected_literal(single.span());
return None;
};
Some(AttributeKind::TestRunner(meta.path().0.clone()))
}
}
pub(crate) struct RustcTestMarkerParser;
impl<S: Stage> SingleAttributeParser<S> for RustcTestMarkerParser {
const PATH: &[Symbol] = &[sym::rustc_test_marker];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Const),
Allow(Target::Fn),
Allow(Target::Static),
]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "test_path");
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::rustc_test_marker));
return None;
};
let Some(value_str) = name_value.value_as_str() else {
cx.expected_string_literal(name_value.value_span, None);
return None;
};
if value_str.as_str().trim().is_empty() {
cx.expected_non_empty_string_literal(name_value.value_span);
return None;
}
Some(AttributeKind::RustcTestMarker(value_str))
}
}

View file

@ -18,11 +18,11 @@ impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
const TEMPLATE: AttributeTemplate = template!(List: &["array, boxed_slice"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let mut array = false;
let mut boxed_slice = false;
let Some(args) = args.list() else {
cx.expected_list(cx.attr_span, args);
cx.expected_list(cx.attr_span);
return None;
};
if args.is_empty() {
@ -50,11 +50,7 @@ impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
cx.duplicate_key(arg.span(), key);
}
}
Some(AttributeKind::RustcSkipDuringMethodDispatch {
array,
boxed_slice,
span: cx.attr_span,
})
Some(AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span: cx.attr_span })
}
}
@ -63,7 +59,16 @@ 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::RustcParenSugar;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ParenSugar;
}
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::Const), Allow(Target::AssocConst)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::TypeConst;
}
// Markers
@ -86,15 +91,15 @@ 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::RustcDenyExplicitImpl;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::DenyExplicitImpl;
}
pub(crate) struct DynIncompatibleTraitParser;
impl<S: Stage> NoArgsAttributeParser<S> for DynIncompatibleTraitParser {
const PATH: &[Symbol] = &[sym::rustc_dyn_incompatible_trait];
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::RustcDynIncompatibleTrait;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::DoNotImplementViaObject;
}
// Specialization
@ -104,7 +109,7 @@ 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::RustcSpecializationTrait;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::SpecializationTrait;
}
pub(crate) struct UnsafeSpecializationMarkerParser;
@ -112,7 +117,7 @@ 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::RustcUnsafeSpecializationMarker;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::UnsafeSpecializationMarker;
}
// Coherence
@ -122,7 +127,7 @@ 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::RustcCoinductive;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Coinductive;
}
pub(crate) struct AllowIncoherentImplParser;
@ -131,7 +136,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for AllowIncoherentImplParser {
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Method(MethodKind::Inherent))]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcAllowIncoherentImpl;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::AllowIncoherentImpl;
}
pub(crate) struct FundamentalParser;

View file

@ -4,6 +4,9 @@ use super::prelude::*;
pub(crate) struct TransparencyParser;
// FIXME(jdonszelmann): make these proper diagnostics
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
const PATH: &[Symbol] = &[sym::rustc_macro_transparency];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
@ -12,26 +15,26 @@ impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
});
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::MacroDef)]);
const TEMPLATE: AttributeTemplate =
template!(NameValueStr: ["transparent", "semiopaque", "opaque"]);
template!(NameValueStr: ["transparent", "semitransparent", "opaque"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
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;
};
match nv.value_as_str() {
Some(sym::transparent) => Some(Transparency::Transparent),
Some(sym::semiopaque) => Some(Transparency::SemiOpaque),
Some(sym::semiopaque | sym::semitransparent) => Some(Transparency::SemiOpaque),
Some(sym::opaque) => Some(Transparency::Opaque),
Some(_) => {
cx.expected_specific_argument_strings(
nv.value_span,
&[sym::transparent, sym::semiopaque, sym::opaque],
&[sym::transparent, sym::semitransparent, sym::opaque],
);
None
}
None => None,
}
.map(AttributeKind::RustcMacroTransparency)
.map(AttributeKind::MacroTransparency)
}
}

View file

@ -5,7 +5,7 @@ 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;
use rustc_span::{Symbol, sym};
use crate::context::{AcceptContext, Stage};
use crate::parser::{ArgParser, NameValueParser};
@ -28,7 +28,38 @@ pub fn parse_version(s: Symbol) -> Option<RustcVersion> {
}
pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
attr.is_doc_comment().is_some() || attr.name().is_some_and(|name| is_builtin_attr_name(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>(
attrs: impl Iterator<Item = &'tcx T>,
symbol: Symbol,
) -> bool {
let doc_attrs = attrs.filter(|attr| attr.has_name(sym::doc));
for attr in doc_attrs {
let Some(values) = attr.meta_item_list() else {
continue;
};
let alias_values = values.iter().filter(|v| v.has_name(sym::alias));
for v in alias_values {
if let Some(nested) = v.meta_item_list() {
// #[doc(alias("foo", "bar"))]
let mut iter = nested.iter().filter_map(|item| item.lit()).map(|item| item.symbol);
if iter.any(|s| s == symbol) {
return true;
}
} else if let Some(meta) = v.meta_item()
&& let Some(lit) = meta.name_value_literal()
{
// #[doc(alias = "foo")]
if lit.symbol == symbol {
return true;
}
}
}
}
false
}
/// Parse a single integer.
@ -39,10 +70,10 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
/// `args` is the parser for the attribute arguments.
pub(crate) fn parse_single_integer<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
args: &ArgParser<'_>,
) -> Option<u128> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span, args);
cx.expected_list(cx.attr_span);
return None;
};
let Some(single) = list.single() else {

View file

@ -1,80 +1,100 @@
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;
use private::Sealed;
use rustc_ast::{AttrStyle, MetaItemLit, NodeId};
use rustc_ast::{AttrStyle, CRATE_NODE_ID, MetaItemLit, NodeId};
use rustc_errors::{Diag, Diagnostic, Level};
use rustc_feature::{AttrSuggestionStyle, AttributeTemplate};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::AttributeLintKind;
use rustc_hir::{AttrPath, HirId};
use rustc_parse::parser::Recovery;
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
use rustc_hir::{AttrPath, CRATE_HIR_ID, HirId};
use rustc_session::Session;
use rustc_session::lint::{Lint, LintId};
use rustc_span::{ErrorGuaranteed, Span, Symbol};
use crate::AttributeParser;
// Glob imports to avoid big, bitrotty import lists
use crate::attributes::allow_unstable::*;
use crate::attributes::body::*;
use crate::attributes::cfi_encoding::*;
use crate::attributes::codegen_attrs::*;
use crate::attributes::confusables::*;
use crate::attributes::crate_level::*;
use crate::attributes::debugger::*;
use crate::attributes::deprecation::*;
use crate::attributes::do_not_recommend::*;
use crate::attributes::doc::*;
use crate::attributes::dummy::*;
use crate::attributes::inline::*;
use crate::attributes::instruction_set::*;
use crate::attributes::link_attrs::*;
use crate::attributes::lint_helpers::*;
use crate::attributes::loop_match::*;
use crate::attributes::macro_attrs::*;
use crate::attributes::must_not_suspend::*;
use crate::attributes::must_use::*;
use crate::attributes::no_implicit_prelude::*;
use crate::attributes::no_link::*;
use crate::attributes::non_exhaustive::*;
use crate::attributes::allow_unstable::{
AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser,
};
use crate::attributes::body::CoroutineParser;
use crate::attributes::codegen_attrs::{
ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser,
NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser,
RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, TargetFeatureParser,
TrackCallerParser, UsedParser,
};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::crate_level::{
CrateNameParser, MoveSizeLimitParser, NoCoreParser, NoStdParser, PatternComplexityLimitParser,
RecursionLimitParser, RustcCoherenceIsCoreParser, TypeLengthLimitParser,
WindowsSubsystemParser,
};
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,
LinkParser, LinkSectionParser, LinkageParser, StdInternalSymbolParser,
};
use crate::attributes::lint_helpers::{
AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser,
RustcShouldNotBeCalledOnConstItems,
};
use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
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::*;
use crate::attributes::proc_macro_attrs::*;
use crate::attributes::prototype::*;
use crate::attributes::repr::*;
use crate::attributes::rustc_allocator::*;
use crate::attributes::rustc_dump::*;
use crate::attributes::rustc_internal::*;
use crate::attributes::semantics::*;
use crate::attributes::stability::*;
use crate::attributes::test_attrs::*;
use crate::attributes::traits::*;
use crate::attributes::transparency::*;
use crate::attributes::pin_v2::PinV2Parser;
use crate::attributes::proc_macro_attrs::{
ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
};
use crate::attributes::prototype::CustomMirParser;
use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser};
use crate::attributes::rustc_internal::{
RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, RustcMainParser,
RustcObjectLifetimeDefaultParser, RustcSimdMonomorphizeLaneLimitParser,
};
use crate::attributes::semantics::MayDangleParser;
use crate::attributes::stability::{
BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
};
use crate::attributes::test_attrs::{IgnoreParser, ShouldPanicParser};
use crate::attributes::traits::{
AllowIncoherentImplParser, CoinductiveParser, 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, RefPathParser};
use crate::parser::{ArgParser, PathParser};
use crate::session_diagnostics::{
AttributeParseError, AttributeParseErrorReason, ParsedDescription,
AttributeParseError, AttributeParseErrorReason, ParsedDescription, UnknownMetaItem,
};
use crate::target_checking::AllowedTargets;
type GroupType<S> = LazyLock<GroupTypeInner<S>>;
pub(super) struct GroupTypeInner<S: Stage> {
pub(super) accepters: BTreeMap<&'static [Symbol], GroupTypeInnerAccept<S>>,
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,
pub(super) finalizer: FinalizeFn<S>,
}
pub(crate) type AcceptFn<S> =
Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser) + Send + Sync>;
pub(crate) type FinalizeFn<S> =
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 {
@ -102,7 +122,8 @@ macro_rules! attribute_parsers {
@[$stage: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?];
) => {
pub(crate) static $name: GroupType<$stage> = LazyLock::new(|| {
let mut accepters = BTreeMap::<_, GroupTypeInnerAccept<$stage>>::new();
let mut accepts = BTreeMap::<_, Vec<GroupTypeInnerAccept<$stage>>>::new();
let mut finalizes = Vec::<FinalizeFn<$stage>>::new();
$(
{
thread_local! {
@ -110,9 +131,7 @@ macro_rules! attribute_parsers {
};
for (path, template, accept_fn) in <$names>::ATTRIBUTES {
match accepters.entry(*path) {
Entry::Vacant(e) => {
e.insert(GroupTypeInnerAccept {
accepts.entry(*path).or_default().push(GroupTypeInnerAccept {
template: *template,
accept_fn: Box::new(|cx, args| {
STATE_OBJECT.with_borrow_mut(|s| {
@ -120,19 +139,17 @@ macro_rules! attribute_parsers {
})
}),
allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS,
finalizer: Box::new(|cx| {
let state = STATE_OBJECT.take();
state.finalize(cx)
})
});
}
Entry::Occupied(_) => panic!("Attribute {path:?} has multiple accepters"),
}
}
finalizes.push(Box::new(|cx| {
let state = STATE_OBJECT.take();
state.finalize(cx)
}));
}
)*
GroupTypeInner { accepters }
GroupTypeInner { accepters:accepts, finalizers:finalizes }
});
};
}
@ -144,10 +161,8 @@ attribute_parsers!(
BodyStabilityParser,
ConfusablesParser,
ConstStabilityParser,
DocParser,
MacroUseParser,
NakedParser,
RustcCguTestAttributeParser,
StabilityParser,
UsedParser,
// tidy-alphabetical-end
@ -155,73 +170,46 @@ attribute_parsers!(
// tidy-alphabetical-start
Combine<AllowConstFnUnstableParser>,
Combine<AllowInternalUnstableParser>,
Combine<CrateTypeParser>,
Combine<DebuggerViualizerParser>,
Combine<ForceTargetFeatureParser>,
Combine<LinkParser>,
Combine<ReprParser>,
Combine<RustcCleanParser>,
Combine<RustcLayoutParser>,
Combine<RustcMirParser>,
Combine<RustcThenThisWouldNeedParser>,
Combine<TargetFeatureParser>,
Combine<UnstableFeatureBoundParser>,
// tidy-alphabetical-end
// tidy-alphabetical-start
Single<CfiEncodingParser>,
Single<CollapseDebugInfoParser>,
Single<CoverageParser>,
Single<CrateNameParser>,
Single<CustomMirParser>,
Single<DeprecationParser>,
Single<DoNotRecommendParser>,
Single<DummyParser>,
Single<ExportNameParser>,
Single<IgnoreParser>,
Single<InlineParser>,
Single<InstructionSetParser>,
Single<LangParser>,
Single<LinkNameParser>,
Single<LinkOrdinalParser>,
Single<LinkSectionParser>,
Single<LinkageParser>,
Single<MacroExportParser>,
Single<MoveSizeLimitParser>,
Single<MustNotSuspendParser>,
Single<MustUseParser>,
Single<ObjcClassParser>,
Single<ObjcSelectorParser>,
Single<OptimizeParser>,
Single<PatchableFunctionEntryParser>,
Single<PathAttributeParser>,
Single<PatternComplexityLimitParser>,
Single<ProcMacroDeriveParser>,
Single<RecursionLimitParser>,
Single<ReexportTestHarnessMainParser>,
Single<RustcAbiParser>,
Single<RustcAllocatorZeroedVariantParser>,
Single<RustcBuiltinMacroParser>,
Single<RustcDefPath>,
Single<RustcDeprecatedSafe2024Parser>,
Single<RustcDiagnosticItemParser>,
Single<RustcForceInlineParser>,
Single<RustcIfThisChangedParser>,
Single<RustcLayoutScalarValidRangeEndParser>,
Single<RustcLayoutScalarValidRangeStartParser>,
Single<RustcLegacyConstGenericsParser>,
Single<RustcLintOptDenyFieldAccessParser>,
Single<RustcMustImplementOneOfParser>,
Single<RustcNeverTypeOptionsParser>,
Single<RustcReservationImplParser>,
Single<RustcScalableVectorParser>,
Single<RustcObjectLifetimeDefaultParser>,
Single<RustcSimdMonomorphizeLaneLimitParser>,
Single<RustcSymbolName>,
Single<RustcTestMarkerParser>,
Single<SanitizeParser>,
Single<ShouldPanicParser>,
Single<SkipDuringMethodDispatchParser>,
Single<TestRunnerParser>,
Single<TransparencyParser>,
Single<TypeLengthLimitParser>,
Single<WindowsSubsystemParser>,
@ -231,14 +219,11 @@ attribute_parsers!(
Single<WithoutArgs<AutomaticallyDerivedParser>>,
Single<WithoutArgs<CoinductiveParser>>,
Single<WithoutArgs<ColdParser>>,
Single<WithoutArgs<CompilerBuiltinsParser>>,
Single<WithoutArgs<ConstContinueParser>>,
Single<WithoutArgs<ConstStabilityIndirectParser>>,
Single<WithoutArgs<CoroutineParser>>,
Single<WithoutArgs<DefaultLibAllocatorParser>>,
Single<WithoutArgs<DenyExplicitImplParser>>,
Single<WithoutArgs<DynIncompatibleTraitParser>>,
Single<WithoutArgs<EiiForeignItemParser>>,
Single<WithoutArgs<DoNotImplementViaObjectParser>>,
Single<WithoutArgs<ExportStableParser>>,
Single<WithoutArgs<FfiConstParser>>,
Single<WithoutArgs<FfiPureParser>>,
@ -247,75 +232,26 @@ attribute_parsers!(
Single<WithoutArgs<MacroEscapeParser>>,
Single<WithoutArgs<MarkerParser>>,
Single<WithoutArgs<MayDangleParser>>,
Single<WithoutArgs<NeedsAllocatorParser>>,
Single<WithoutArgs<NeedsPanicRuntimeParser>>,
Single<WithoutArgs<NoBuiltinsParser>>,
Single<WithoutArgs<NoCoreParser>>,
Single<WithoutArgs<NoImplicitPreludeParser>>,
Single<WithoutArgs<NoLinkParser>>,
Single<WithoutArgs<NoMainParser>>,
Single<WithoutArgs<NoMangleParser>>,
Single<WithoutArgs<NoStdParser>>,
Single<WithoutArgs<NonExhaustiveParser>>,
Single<WithoutArgs<PanicHandlerParser>>,
Single<WithoutArgs<PanicRuntimeParser>>,
Single<WithoutArgs<ParenSugarParser>>,
Single<WithoutArgs<PassByValueParser>>,
Single<WithoutArgs<PinV2Parser>>,
Single<WithoutArgs<PointeeParser>>,
Single<WithoutArgs<PreludeImportParser>>,
Single<WithoutArgs<ProcMacroAttributeParser>>,
Single<WithoutArgs<ProcMacroParser>>,
Single<WithoutArgs<ProfilerRuntimeParser>>,
Single<WithoutArgs<PubTransparentParser>>,
Single<WithoutArgs<RustcAllocatorParser>>,
Single<WithoutArgs<RustcAllocatorZeroedParser>>,
Single<WithoutArgs<RustcCaptureAnalysisParser>>,
Single<WithoutArgs<RustcCoherenceIsCoreParser>>,
Single<WithoutArgs<RustcConversionSuggestionParser>>,
Single<WithoutArgs<RustcDeallocatorParser>>,
Single<WithoutArgs<RustcDelayedBugFromInsideQueryParser>>,
Single<WithoutArgs<RustcDoNotConstCheckParser>>,
Single<WithoutArgs<RustcDumpDefParentsParser>>,
Single<WithoutArgs<RustcDumpItemBoundsParser>>,
Single<WithoutArgs<RustcDumpPredicatesParser>>,
Single<WithoutArgs<RustcDumpUserArgsParser>>,
Single<WithoutArgs<RustcDumpVtableParser>>,
Single<WithoutArgs<RustcEffectiveVisibilityParser>>,
Single<WithoutArgs<RustcEvaluateWhereClausesParser>>,
Single<WithoutArgs<RustcHasIncoherentInherentImplsParser>>,
Single<WithoutArgs<RustcHiddenTypeOfOpaquesParser>>,
Single<WithoutArgs<RustcInsignificantDtorParser>>,
Single<WithoutArgs<RustcIntrinsicConstStableIndirectParser>>,
Single<WithoutArgs<RustcIntrinsicParser>>,
Single<WithoutArgs<RustcLintOptTyParser>>,
Single<WithoutArgs<RustcLintQueryInstabilityParser>>,
Single<WithoutArgs<RustcLintUntrackedQueryInformationParser>>,
Single<WithoutArgs<RustcMainParser>>,
Single<WithoutArgs<RustcNeverReturnsNullPointerParser>>,
Single<WithoutArgs<RustcNoImplicitAutorefsParser>>,
Single<WithoutArgs<RustcNoImplicitBoundsParser>>,
Single<WithoutArgs<RustcNoMirInlineParser>>,
Single<WithoutArgs<RustcNonConstTraitMethodParser>>,
Single<WithoutArgs<RustcNonnullOptimizationGuaranteedParser>>,
Single<WithoutArgs<RustcNounwindParser>>,
Single<WithoutArgs<RustcObjectLifetimeDefaultParser>>,
Single<WithoutArgs<RustcOffloadKernelParser>>,
Single<WithoutArgs<RustcOutlivesParser>>,
Single<WithoutArgs<RustcPassIndirectlyInNonRusticAbisParser>>,
Single<WithoutArgs<RustcPreserveUbChecksParser>>,
Single<WithoutArgs<RustcProcMacroDeclsParser>>,
Single<WithoutArgs<RustcReallocatorParser>>,
Single<WithoutArgs<RustcRegionsParser>>,
Single<WithoutArgs<RustcShouldNotBeCalledOnConstItems>>,
Single<WithoutArgs<RustcStrictCoherenceParser>>,
Single<WithoutArgs<RustcTrivialFieldReadsParser>>,
Single<WithoutArgs<RustcVarianceOfOpaquesParser>>,
Single<WithoutArgs<RustcVarianceParser>>,
Single<WithoutArgs<SpecializationTraitParser>>,
Single<WithoutArgs<StdInternalSymbolParser>>,
Single<WithoutArgs<ThreadLocalParser>>,
Single<WithoutArgs<TrackCallerParser>>,
Single<WithoutArgs<TypeConstParser>>,
Single<WithoutArgs<UnsafeSpecializationMarkerParser>>,
// tidy-alphabetical-end
];
@ -341,6 +277,8 @@ pub trait Stage: Sized + 'static + Sealed {
) -> ErrorGuaranteed;
fn should_emit(&self) -> ShouldEmit;
fn id_is_crate_root(id: Self::Id) -> bool;
}
// allow because it's a sealed trait
@ -362,6 +300,10 @@ impl Stage for Early {
fn should_emit(&self) -> ShouldEmit {
self.emit_errors
}
fn id_is_crate_root(id: Self::Id) -> bool {
id == CRATE_NODE_ID
}
}
// allow because it's a sealed trait
@ -381,7 +323,11 @@ impl Stage for Late {
}
fn should_emit(&self) -> ShouldEmit {
ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }
ShouldEmit::ErrorsAndLints
}
fn id_is_crate_root(id: Self::Id) -> bool {
id == CRATE_HIR_ID
}
}
@ -435,19 +381,19 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
/// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing
/// 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: &'static Lint, kind: AttributeLintKind, span: Span) {
pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
if !matches!(
self.stage.should_emit(),
ShouldEmit::ErrorsAndLints { .. } | ShouldEmit::EarlyFatal { also_emit_lints: true }
ShouldEmit::ErrorsAndLints | ShouldEmit::EarlyFatal { also_emit_lints: true }
) {
return;
}
(self.emit_lint)(LintId::of(lint), span, kind);
let id = self.target_id;
(self.emit_lint)(AttributeLint { id, span, kind: lint });
}
pub(crate) fn warn_unused_duplicate(&mut self, used_span: Span, unused_span: Span) {
self.emit_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
AttributeLintKind::UnusedDuplicate {
this: unused_span,
other: used_span,
@ -463,7 +409,6 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
unused_span: Span,
) {
self.emit_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
AttributeLintKind::UnusedDuplicate {
this: unused_span,
other: used_span,
@ -475,20 +420,13 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
}
impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
fn emit_parse_error(
pub(crate) fn unknown_key(
&self,
span: Span,
reason: AttributeParseErrorReason<'_>,
found: String,
options: &'static [&'static str],
) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason,
suggestions: self.suggestions(),
})
self.emit_err(UnknownMetaItem { span, item: found, expected: options })
}
/// error that a string literal was expected.
@ -500,101 +438,133 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
span: Span,
actual_literal: Option<&MetaItemLit>,
) -> ErrorGuaranteed {
self.emit_parse_error(
self.emit_err(AttributeParseError {
span,
AttributeParseErrorReason::ExpectedStringLiteral {
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedStringLiteral {
byte_string: actual_literal.and_then(|i| {
i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
}),
},
)
}
/// Error that a filename string literal was expected.
pub(crate) fn expected_filename_literal(&self, span: Span) {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedFilenameLiteral);
suggestions: self.suggestions(),
})
}
pub(crate) fn expected_integer_literal(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral)
}
pub(crate) fn expected_integer_literal_in_range(
&self,
span: Span,
lower_bound: isize,
upper_bound: isize,
) -> ErrorGuaranteed {
self.emit_parse_error(
self.emit_err(AttributeParseError {
span,
AttributeParseErrorReason::ExpectedIntegerLiteralInRange { lower_bound, upper_bound },
)
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedIntegerLiteral,
suggestions: self.suggestions(),
})
}
pub(crate) fn expected_list(&self, span: Span, args: &ArgParser) -> ErrorGuaranteed {
let span = match args {
ArgParser::NoArgs => span,
ArgParser::List(list) => list.span,
ArgParser::NameValue(nv) => nv.args_span(),
};
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedList)
}
pub(crate) fn expected_list_with_num_args_or_more(
&self,
args: usize,
span: Span,
) -> ErrorGuaranteed {
self.emit_parse_error(
pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
AttributeParseErrorReason::ExpectedListWithNumArgsOrMore { args },
)
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedList,
suggestions: self.suggestions(),
})
}
pub(crate) fn expected_list_or_no_args(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedListOrNoArgs)
}
pub(crate) fn expected_nv_or_no_args(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValueOrNoArgs)
}
pub(crate) fn expected_non_empty_string_literal(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNonEmptyStringLiteral)
}
pub(crate) fn expected_no_args(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNoArgs)
pub(crate) fn expected_no_args(&self, args_span: Span) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span: args_span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedNoArgs,
suggestions: self.suggestions(),
})
}
/// emit an error that a `name` was expected here
pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIdentifier)
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedIdentifier,
suggestions: self.suggestions(),
})
}
/// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
/// a nicer error message talking about the specific name that was found lacking a value.
pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValue(name))
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedNameValue(name),
suggestions: self.suggestions(),
})
}
/// emit an error that a `name = value` pair was found where that name was already seen.
pub(crate) fn duplicate_key(&self, span: Span, key: Symbol) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::DuplicateKey(key))
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::DuplicateKey(key),
suggestions: self.suggestions(),
})
}
/// an error that should be emitted when a [`MetaItemOrLitParser`](crate::parser::MetaItemOrLitParser)
/// was expected *not* to be a literal, but instead a meta item.
pub(crate) fn unexpected_literal(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::UnexpectedLiteral)
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::UnexpectedLiteral,
suggestions: self.suggestions(),
})
}
pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedSingleArgument)
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedSingleArgument,
suggestions: self.suggestions(),
})
}
pub(crate) fn expected_at_least_one_argument(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedAtLeastOneArgument)
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument,
suggestions: self.suggestions(),
})
}
/// produces an error along the lines of `expected one of [foo, meow]`
@ -603,14 +573,19 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
span: Span,
possibilities: &[Symbol],
) -> ErrorGuaranteed {
self.emit_parse_error(
self.emit_err(AttributeParseError {
span,
AttributeParseErrorReason::ExpectedSpecificArgument {
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: false,
list: false,
},
)
suggestions: self.suggestions(),
})
}
/// produces an error along the lines of `expected one of [foo, meow] as an argument`.
@ -620,14 +595,19 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
span: Span,
possibilities: &[Symbol],
) -> ErrorGuaranteed {
self.emit_parse_error(
self.emit_err(AttributeParseError {
span,
AttributeParseErrorReason::ExpectedSpecificArgument {
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: false,
list: true,
},
)
suggestions: self.suggestions(),
})
}
/// produces an error along the lines of `expected one of ["foo", "meow"]`
@ -636,36 +616,30 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
span: Span,
possibilities: &[Symbol],
) -> ErrorGuaranteed {
self.emit_parse_error(
self.emit_err(AttributeParseError {
span,
AttributeParseErrorReason::ExpectedSpecificArgument {
attr_span: self.attr_span,
template: self.template.clone(),
path: self.attr_path.clone(),
description: self.parsed_description,
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: true,
list: false,
},
)
suggestions: self.suggestions(),
})
}
pub(crate) fn warn_empty_attribute(&mut self, span: Span) {
let attr_path = self.attr_path.clone().to_string();
let attr_path = self.attr_path.clone();
let valid_without_list = self.template.word;
self.emit_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
AttributeLintKind::EmptyAttribute { first_span: span, attr_path, valid_without_list },
span,
);
}
pub(crate) fn warn_ill_formed_attribute_input(&mut self, lint: &'static Lint) {
let suggestions = self.suggestions();
let span = self.attr_span;
self.emit_lint(
lint,
AttributeLintKind::IllFormedAttributeInput { suggestions, docs: None },
span,
);
}
pub(crate) fn suggestions(&self) -> Vec<String> {
let style = match self.parsed_description {
// If the outer and inner spans are equal, we are parsing an embedded attribute
@ -704,11 +678,10 @@ pub struct SharedContext<'p, 'sess, S: Stage> {
pub(crate) cx: &'p mut AttributeParser<'sess, S>,
/// The span of the syntactical component this attribute was applied to
pub(crate) target_span: Span,
pub(crate) target: rustc_hir::Target,
/// 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,
/// The second argument of the closure is a [`NodeId`] if `S` is `Early` and a [`HirId`] if `S`
/// is `Late` and is the ID of the syntactical component this attribute was applied to.
pub(crate) emit_lint: &'p mut dyn FnMut(LintId, Span, AttributeLintKind),
pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>),
}
/// Context given to every attribute parser during finalization.
@ -724,7 +697,7 @@ pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> {
///
/// Usually, you should use normal attribute parsing logic instead,
/// especially when making a *denylist* of other attributes.
pub(crate) all_attrs: &'p [RefPathParser<'p>],
pub(crate) all_attrs: &'p [PathParser<'p>],
}
impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> {
@ -770,18 +743,9 @@ pub enum ShouldEmit {
EarlyFatal { also_emit_lints: bool },
/// The operation will emit errors and lints.
/// This is usually what you need.
ErrorsAndLints {
/// Whether [`ArgParser`] will attempt to recover from errors.
///
/// Whether it is allowed to recover from bad input (like an invalid literal). Setting
/// this to `Forbidden` will instead return early, and not raise errors except at the top
/// level (in [`ArgParser::from_attr_args`]).
recovery: Recovery,
},
/// The operation will *not* emit errors and lints.
///
/// The parser can still call `delay_bug`, so you *must* ensure that this operation will also be
/// called with `ShouldEmit::ErrorsAndLints`.
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::ErrorsAndLints`.
Nothing,
}
@ -790,7 +754,7 @@ impl ShouldEmit {
match self {
ShouldEmit::EarlyFatal { .. } if diag.level() == Level::DelayedBug => diag.emit(),
ShouldEmit::EarlyFatal { .. } => diag.upgrade_to_fatal().emit(),
ShouldEmit::ErrorsAndLints { .. } => diag.emit(),
ShouldEmit::ErrorsAndLints => diag.emit(),
ShouldEmit::Nothing => diag.delay_as_bug(),
}
}

View file

@ -1,53 +0,0 @@
use rustc_ast::EarlyParsedAttribute;
use rustc_ast::attr::data_structures::CfgEntry;
use rustc_hir::Attribute;
use rustc_hir::attrs::AttributeKind;
use rustc_span::{Span, Symbol, sym};
use thin_vec::ThinVec;
pub(crate) const EARLY_PARSED_ATTRIBUTES: &[&[Symbol]] =
&[&[sym::cfg_trace], &[sym::cfg_attr_trace]];
/// This struct contains the state necessary to convert early parsed attributes to hir attributes
/// The only conversion that really happens here is that multiple early parsed attributes are
/// merged into a single hir attribute, representing their combined state.
/// FIXME: We should make this a nice and extendable system if this is going to be used more often
#[derive(Default)]
pub(crate) struct EarlyParsedState {
/// Attribute state for `#[cfg]` trace attributes
cfg_trace: ThinVec<(CfgEntry, Span)>,
/// Attribute state for `#[cfg_attr]` trace attributes
/// The arguments of these attributes is no longer relevant for any later passes, only their presence.
/// So we discard the arguments here.
cfg_attr_trace: bool,
}
impl EarlyParsedState {
pub(crate) fn accept_early_parsed_attribute(
&mut self,
attr_span: Span,
lower_span: impl Copy + Fn(Span) -> Span,
parsed: &EarlyParsedAttribute,
) {
match parsed {
EarlyParsedAttribute::CfgTrace(cfg) => {
let mut cfg = cfg.clone();
cfg.lower_spans(lower_span);
self.cfg_trace.push((cfg, attr_span));
}
EarlyParsedAttribute::CfgAttrTrace => {
self.cfg_attr_trace = true;
}
}
}
pub(crate) fn finalize_early_parsed_attributes(self, attributes: &mut Vec<Attribute>) {
if !self.cfg_trace.is_empty() {
attributes.push(Attribute::Parsed(AttributeKind::CfgTrace(self.cfg_trace)));
}
if self.cfg_attr_trace {
attributes.push(Attribute::Parsed(AttributeKind::CfgAttrTrace));
}
}
}

View file

@ -1,20 +1,17 @@
use std::convert::identity;
use std::borrow::Cow;
use rustc_ast as ast;
use rustc_ast::token::DocFragmentKind;
use rustc_ast::{AttrItemKind, AttrStyle, NodeId, Safety};
use rustc_ast::{AttrStyle, NodeId, Safety};
use rustc_errors::DiagCtxtHandle;
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::AttributeLintKind;
use rustc_hir::lints::AttributeLint;
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target};
use rustc_session::Session;
use rustc_session::lint::{BuiltinLintDiag, LintId};
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
use crate::context::{AcceptContext, FinalizeContext, FinalizeFn, SharedContext, Stage};
use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState};
use crate::parser::{ArgParser, PathParser, RefPathParser};
use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage};
use crate::parser::{ArgParser, MetaItemParser, PathParser};
use crate::session_diagnostics::ParsedDescription;
use crate::{Early, Late, OmitDoc, ShouldEmit};
@ -113,16 +110,12 @@ impl<'sess> AttributeParser<'sess, Early> {
p.parse_attribute_list(
attrs,
target_span,
target_node_id,
target,
OmitDoc::Skip,
std::convert::identity,
|lint_id, span, kind| {
sess.psess.buffer_lint(
lint_id.lint,
span,
target_node_id,
BuiltinLintDiag::AttributeLint(kind),
)
|lint| {
crate::lints::emit_attribute_lint(&lint, sess);
},
)
}
@ -134,10 +127,9 @@ impl<'sess> AttributeParser<'sess, Early> {
attr: &ast::Attribute,
target_span: Span,
target_node_id: NodeId,
target: Target,
features: Option<&'sess Features>,
emit_errors: ShouldEmit,
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser) -> Option<T>,
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> Option<T>,
template: &AttributeTemplate,
) -> Option<T> {
let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
@ -145,28 +137,22 @@ impl<'sess> AttributeParser<'sess, Early> {
};
let parts =
normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
let path = AttrPath::from_ast(&normal_attr.item.path, identity);
let args = ArgParser::from_attr_args(
&normal_attr.item.args.unparsed_ref().unwrap(),
&parts,
&sess.psess,
emit_errors,
)?;
let meta_parser = MetaItemParser::from_attr(normal_attr, &parts, &sess.psess, emit_errors)?;
let path = meta_parser.path();
let args = meta_parser.args();
Self::parse_single_args(
sess,
attr.span,
normal_attr.item.span(),
attr.style,
path,
path.get_attribute_path(),
Some(normal_attr.item.unsafety),
ParsedDescription::Attribute,
target_span,
target_node_id,
target,
features,
emit_errors,
&args,
args,
parse_fn,
template,
)
@ -184,7 +170,6 @@ impl<'sess> AttributeParser<'sess, Early> {
parsed_description: ParsedDescription,
target_span: Span,
target_node_id: NodeId,
target: Target,
features: Option<&'sess Features>,
emit_errors: ShouldEmit,
args: &I,
@ -198,22 +183,23 @@ impl<'sess> AttributeParser<'sess, Early> {
sess,
stage: Early { emit_errors },
};
let mut emit_lint = |lint_id: LintId, span: Span, kind: AttributeLintKind| {
sess.psess.buffer_lint(
lint_id.lint,
span,
target_node_id,
BuiltinLintDiag::AttributeLint(kind),
)
let mut emit_lint = |lint| {
crate::lints::emit_attribute_lint(&lint, sess);
};
if let Some(safety) = attr_safety {
parser.check_attribute_safety(&attr_path, inner_span, safety, &mut emit_lint)
parser.check_attribute_safety(
&attr_path,
inner_span,
safety,
&mut emit_lint,
target_node_id,
)
}
let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
shared: SharedContext {
cx: &mut parser,
target_span,
target,
target_id: target_node_id,
emit_lint: &mut emit_lint,
},
attr_span,
@ -261,16 +247,15 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
&mut self,
attrs: &[ast::Attribute],
target_span: Span,
target_id: S::Id,
target: Target,
omit_doc: OmitDoc,
lower_span: impl Copy + Fn(Span) -> Span,
mut emit_lint: impl FnMut(LintId, Span, AttributeLintKind),
mut emit_lint: impl FnMut(AttributeLint<S::Id>),
) -> Vec<Attribute> {
let mut attributes = Vec::new();
let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
let mut early_parsed_state = EarlyParsedState::default();
let mut finalizers: Vec<&FinalizeFn<S>> = Vec::with_capacity(attrs.len());
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.
@ -285,12 +270,10 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
// 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.
let is_doc_attribute = attr.has_name(sym::doc);
if omit_doc == OmitDoc::Skip && is_doc_attribute {
if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) {
continue;
}
let attr_span = lower_span(attr.span);
match &attr.kind {
ast::AttrKind::DocComment(comment_kind, symbol) => {
if omit_doc == OmitDoc::Skip {
@ -299,83 +282,56 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
attributes.push(Attribute::Parsed(AttributeKind::DocComment {
style: attr.style,
kind: DocFragmentKind::Sugared(*comment_kind),
span: attr_span,
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(&n.item.path));
attr_paths.push(PathParser(Cow::Borrowed(&n.item.path)));
let attr_path = AttrPath::from_ast(&n.item.path, lower_span);
let args = match &n.item.args {
AttrItemKind::Unparsed(args) => args,
AttrItemKind::Parsed(parsed) => {
early_parsed_state
.accept_early_parsed_attribute(attr_span, lower_span, parsed);
continue;
}
};
self.check_attribute_safety(
&attr_path,
lower_span(n.item.span()),
n.item.unsafety,
&mut emit_lint,
target_id,
);
let parts =
n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
if let Some(accept) = S::parsers().accepters.get(parts.as_slice()) {
let Some(args) = ArgParser::from_attr_args(
args,
if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) {
let Some(parser) = MetaItemParser::from_attr(
n,
&parts,
&self.sess.psess,
self.stage.should_emit(),
) else {
continue;
};
// Special-case handling for `#[doc = "..."]`: if we go through with
// `DocParser`, the order of doc comments will be messed up because `///`
// doc comments are added into `attributes` whereas attributes parsed with
// `DocParser` are added into `parsed_attributes` which are then appended
// to `attributes`. So if you have:
//
// /// bla
// #[doc = "a"]
// /// blob
//
// You would get:
//
// bla
// blob
// a
if is_doc_attribute
&& let ArgParser::NameValue(nv) = &args
// If not a string key/value, it should emit an error, but to make
// things simpler, it's handled in `DocParser` because it's simpler to
// emit an error with `AcceptContext`.
&& let Some(comment) = nv.value_as_str()
{
attributes.push(Attribute::Parsed(AttributeKind::DocComment {
style: attr.style,
kind: DocFragmentKind::Raw(nv.value_span),
span: attr_span,
comment,
}));
continue;
}
let args = parser.args();
for accept in accepts {
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
shared: SharedContext {
cx: self,
target_span,
target,
target_id,
emit_lint: &mut emit_lint,
},
attr_span,
attr_span: lower_span(attr.span),
inner_span: lower_span(n.item.span()),
attr_style: attr.style,
parsed_description: ParsedDescription::Attribute,
@ -383,12 +339,11 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
attr_path: attr_path.clone(),
};
(accept.accept_fn)(&mut cx, &args);
finalizers.push(&accept.finalizer);
(accept.accept_fn)(&mut cx, args);
if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
Self::check_target(&accept.allowed_targets, target, &mut cx);
}
}
} else {
// If we're here, we must be compiling a tool attribute... Or someone
// forgot to parse their fancy new attribute. Let's warn them in any case.
@ -407,43 +362,39 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
attributes.push(Attribute::Unparsed(Box::new(AttrItem {
path: attr_path.clone(),
args: self
.lower_attr_args(n.item.args.unparsed_ref().unwrap(), lower_span),
args: self.lower_attr_args(&n.item.args, lower_span),
id: HashIgnoredAttrId { attr_id: attr.id },
style: attr.style,
span: attr_span,
span: lower_span(attr.span),
})));
}
}
}
}
early_parsed_state.finalize_early_parsed_attributes(&mut attributes);
for f in &finalizers {
let mut parsed_attributes = Vec::new();
for f in &S::parsers().finalizers {
if let Some(attr) = f(&mut FinalizeContext {
shared: SharedContext { cx: self, target_span, target, emit_lint: &mut emit_lint },
shared: SharedContext {
cx: self,
target_span,
target_id,
emit_lint: &mut emit_lint,
},
all_attrs: &attr_paths,
}) {
attributes.push(Attribute::Parsed(attr));
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 {
/// The list of attributes that are parsed attributes,
/// even though they don't have a parser in `Late::parsers()`
const SPECIAL_ATTRIBUTES: &[&[Symbol]] = &[
// Cfg attrs are removed after being early-parsed, so don't need to be in the parser list
&[sym::cfg],
&[sym::cfg_attr],
];
Late::parsers().accepters.contains_key(path)
|| EARLY_PARSED_ATTRIBUTES.contains(&path)
|| SPECIAL_ATTRIBUTES.contains(&path)
}
fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {

View file

@ -78,8 +78,6 @@
// tidy-alphabetical-start
#![feature(decl_macro)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![recursion_limit = "256"]
// tidy-alphabetical-end
@ -99,7 +97,7 @@ mod interface;
/// like lists or name-value pairs.
pub mod parser;
mod early_parsed;
mod lints;
mod safety;
mod session_diagnostics;
mod target_checking;
@ -108,8 +106,12 @@ pub mod validate_attr;
pub use attributes::cfg::{
CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg, parse_cfg_attr, parse_cfg_entry,
};
pub use attributes::cfg_old::*;
pub use attributes::cfg_select::*;
pub use attributes::util::{is_builtin_attr, parse_version};
pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version};
pub use context::{Early, Late, OmitDoc, ShouldEmit};
pub use interface::AttributeParser;
pub use lints::emit_attribute_lint;
pub use session_diagnostics::ParsedDescription;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }

View file

@ -0,0 +1,114 @@
use std::borrow::Cow;
use rustc_errors::{DiagArgValue, LintEmitter};
use rustc_hir::Target;
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
use rustc_span::sym;
use crate::session_diagnostics;
pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<L::Id>, lint_emitter: L) {
let AttributeLint { id, span, kind } = lint;
match kind {
&AttributeLintKind::UnusedDuplicate { this, other, warning } => lint_emitter
.emit_node_span_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
*id,
*span,
session_diagnostics::UnusedDuplicate { this, other, warning },
),
AttributeLintKind::IllFormedAttributeInput { suggestions } => {
lint_emitter.emit_node_span_lint(
rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT,
*id,
*span,
session_diagnostics::IllFormedAttributeInput {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
),
},
);
}
AttributeLintKind::InvalidMacroExportArguments { suggestions } => lint_emitter
.emit_node_span_lint(
rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS,
*id,
*span,
session_diagnostics::IllFormedAttributeInput {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
),
},
),
AttributeLintKind::EmptyAttribute { first_span, attr_path, valid_without_list } => {
lint_emitter.emit_node_span_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
*id,
*first_span,
session_diagnostics::EmptyAttributeList {
attr_span: *first_span,
attr_path: attr_path.clone(),
valid_without_list: *valid_without_list,
},
)
}
AttributeLintKind::InvalidTarget { name, target, applied, only } => lint_emitter
.emit_node_span_lint(
// This check is here because `deprecated` had its own lint group and removing this would be a breaking change
if name.segments[0].name == sym::deprecated
&& ![
Target::Closure,
Target::Expression,
Target::Statement,
Target::Arm,
Target::MacroCall,
]
.contains(target)
{
rustc_session::lint::builtin::USELESS_DEPRECATED
} else {
rustc_session::lint::builtin::UNUSED_ATTRIBUTES
},
*id,
*span,
session_diagnostics::InvalidTargetLint {
name: name.clone(),
target: target.plural_name(),
applied: DiagArgValue::StrListSepByAnd(
applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(),
),
only,
attr_span: *span,
},
),
&AttributeLintKind::InvalidStyle { ref name, is_used_as_inner, target, target_span } => {
lint_emitter.emit_node_span_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
*id,
*span,
session_diagnostics::InvalidAttrStyle {
name: name.clone(),
is_used_as_inner,
target_span: (!is_used_as_inner).then_some(target_span),
target,
},
)
}
&AttributeLintKind::UnsafeAttrOutsideUnsafe {
attribute_name_span,
sugg_spans: (left, right),
} => lint_emitter.emit_node_span_lint(
rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE,
*id,
*span,
session_diagnostics::UnsafeAttrOutsideUnsafeLint {
span: attribute_name_span,
suggestion: session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion { left, right },
},
),
}
}

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