Merge pull request #21268 from rust-lang/rustc-pull

minor: Rustc pull update
This commit is contained in:
Laurențiu Nicola
2025-12-15 09:49:44 +00:00
committed by GitHub
2306 changed files with 41193 additions and 24869 deletions
+1
View File
@@ -0,0 +1 @@
custom: ["rust-lang.org/funding"]
+1
View File
@@ -313,6 +313,7 @@ jobs:
needs: [ calculate_matrix, job ]
# !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')) && 'bors') || '' }}
steps:
- name: checkout the source code
uses: actions/checkout@v5
+2
View File
@@ -55,6 +55,8 @@ 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
+6 -1
View File
@@ -43,6 +43,7 @@ Andre Bogus <bogusandre@gmail.com>
Andre Bogus <bogusandre@gmail.com> <andre.bogus@aleph-alpha.de>
Andre Bogus <bogusandre@gmail.com> <andre.bogus@ankordata.de>
Andrea Ciliberti <meziu210@icloud.com>
Andreas Gal <gal@mozilla.com> <andreas.gal@gmail.com>
Andreas Jonson <andjo403@users.noreply.github.com>
Andrew Gauger <andygauge@gmail.com>
@@ -82,12 +83,15 @@ 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>
Björn Steinbrink <bsteinbr@gmail.com> <B.Steinbrink@gmx.de>
blake2-ppc <ulrik.sverdrup@gmail.com> <blake2-ppc>
blyxyas <blyxyas@gmail.com> Alejandra González <blyxyas@gmail.com>
Alejandra González <blyxyas@goose.love> blyxyas <blyxyas@gmail.com>
Alejandra González <blyxyas@goose.love> blyxyas <blyxyas@goose.love>
Alejandra González <blyxyas@goose.love> Alejandra González <blyxyas@gmail.com>
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>
@@ -427,6 +431,7 @@ 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>
+13 -12
View File
@@ -74,9 +74,9 @@ dependencies = [
[[package]]
name = "annotate-snippets"
version = "0.12.9"
version = "0.12.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44baf24dd94e781f74dfe67ffee75a09a57971ddf0f615a178b4f6d404b48ff"
checksum = "15580ece6ea97cbf832d60ba19c021113469480852c6a2a6beb0db28f097bf1f"
dependencies = [
"anstyle",
"memchr",
@@ -621,7 +621,7 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
[[package]]
name = "clippy"
version = "0.1.93"
version = "0.1.94"
dependencies = [
"anstream",
"askama",
@@ -648,7 +648,7 @@ dependencies = [
[[package]]
name = "clippy_config"
version = "0.1.93"
version = "0.1.94"
dependencies = [
"clippy_utils",
"itertools",
@@ -671,7 +671,7 @@ dependencies = [
[[package]]
name = "clippy_lints"
version = "0.1.93"
version = "0.1.94"
dependencies = [
"arrayvec",
"cargo_metadata 0.18.1",
@@ -703,7 +703,7 @@ dependencies = [
[[package]]
name = "clippy_utils"
version = "0.1.93"
version = "0.1.94"
dependencies = [
"arrayvec",
"itertools",
@@ -1107,7 +1107,7 @@ dependencies = [
[[package]]
name = "declare_clippy_lint"
version = "0.1.93"
version = "0.1.94"
[[package]]
name = "derive-where"
@@ -3838,7 +3838,7 @@ dependencies = [
name = "rustc_errors"
version = "0.0.0"
dependencies = [
"annotate-snippets 0.12.9",
"annotate-snippets 0.12.10",
"anstream",
"anstyle",
"derive_setters",
@@ -3945,6 +3945,7 @@ dependencies = [
"rustc_hashes",
"rustc_hir_id",
"rustc_index",
"rustc_lint_defs",
"rustc_macros",
"rustc_serialize",
"rustc_span",
@@ -3962,7 +3963,6 @@ dependencies = [
"rustc_abi",
"rustc_arena",
"rustc_ast",
"rustc_attr_parsing",
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
@@ -3970,6 +3970,7 @@ dependencies = [
"rustc_hir",
"rustc_index",
"rustc_infer",
"rustc_lint",
"rustc_lint_defs",
"rustc_macros",
"rustc_middle",
@@ -4010,7 +4011,6 @@ dependencies = [
"itertools",
"rustc_abi",
"rustc_ast",
"rustc_attr_parsing",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
@@ -4432,7 +4432,6 @@ dependencies = [
"rustc_abi",
"rustc_ast",
"rustc_ast_lowering",
"rustc_ast_pretty",
"rustc_attr_parsing",
"rustc_data_structures",
"rustc_errors",
@@ -4631,7 +4630,6 @@ dependencies = [
name = "rustc_session"
version = "0.0.0"
dependencies = [
"bitflags",
"getopts",
"libc",
"rand 0.9.2",
@@ -4658,6 +4656,7 @@ dependencies = [
name = "rustc_span"
version = "0.0.0"
dependencies = [
"bitflags",
"blake3",
"derive-where",
"indexmap",
@@ -4869,6 +4868,7 @@ dependencies = [
"indexmap",
"itertools",
"minifier",
"proc-macro2",
"pulldown-cmark-escape",
"regex",
"rustdoc-json-types",
@@ -5615,6 +5615,7 @@ dependencies = [
"semver",
"serde",
"similar",
"tempfile",
"termcolor",
"toml 0.7.8",
"walkdir",
+85
View File
@@ -1,3 +1,88 @@
Version 1.92.0 (2025-12-11)
==========================
<a id="1.92.0-Language"></a>
Language
--------
- [Document `MaybeUninit` representation and validity](https://github.com/rust-lang/rust/pull/140463)
- [Allow `&raw [mut | const]` for union field in safe code](https://github.com/rust-lang/rust/pull/141469)
- [Prefer item bounds of associated types over where-bounds for auto-traits and `Sized`](https://github.com/rust-lang/rust/pull/144064)
- [Do not materialize `X` in `[X; 0]` when `X` is unsizing a const](https://github.com/rust-lang/rust/pull/145277)
- [Support combining `#[track_caller]` and `#[no_mangle]` (requires every declaration specifying `#[track_caller]` as well)](https://github.com/rust-lang/rust/pull/145724)
- [Make never type lints `never_type_fallback_flowing_into_unsafe` and `dependency_on_unit_never_type_fallback` deny-by-default](https://github.com/rust-lang/rust/pull/146167)
- [Allow specifying multiple bounds for same associated item, except in trait objects](https://github.com/rust-lang/rust/pull/146593)
- [Slightly strengthen higher-ranked region handling in coherence](https://github.com/rust-lang/rust/pull/146725)
- [The `unused_must_use` lint no longer warns on `Result<(), Uninhabited>` (for instance, `Result<(), !>`), or `ControlFlow<Uninhabited, ()>`](https://github.com/rust-lang/rust/pull/147382). This avoids having to check for an error that can never happen.
<a id="1.92.0-Compiler"></a>
Compiler
--------
- [Make `mips64el-unknown-linux-muslabi64` link dynamically](https://github.com/rust-lang/rust/pull/146858)
- [Remove current code for embedding command-line args in PDB](https://github.com/rust-lang/rust/pull/147022)
Command-line information is typically not needed by debugging tools, and the removed code
was causing problems for incremental builds even on targets that don't use PDB debuginfo.
<a id="1.92.0-Libraries"></a>
Libraries
---------
- [Specialize `Iterator::eq{_by}` for `TrustedLen` iterators](https://github.com/rust-lang/rust/pull/137122)
- [Simplify `Extend` for tuples](https://github.com/rust-lang/rust/pull/138799)
- [Added details to `Debug` for `EncodeWide`](https://github.com/rust-lang/rust/pull/140153).
- [`iter::Repeat::last`](https://github.com/rust-lang/rust/pull/147258) and [`count`](https://github.com/rust-lang/rust/pull/146410) will now panic, rather than looping infinitely.
<a id="1.92.0-Stabilized-APIs"></a>
Stabilized APIs
---------------
- [`NonZero<u{N}>::div_ceil`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.div_ceil)
- [`Location::file_as_c_str`](https://doc.rust-lang.org/stable/std/panic/struct.Location.html#method.file_as_c_str)
- [`RwLockWriteGuard::downgrade`](https://doc.rust-lang.org/stable/std/sync/struct.RwLockWriteGuard.html#method.downgrade)
- [`Box::new_zeroed`](https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.new_zeroed)
- [`Box::new_zeroed_slice`](https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.new_zeroed_slice)
- [`Rc::new_zeroed`](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.new_zeroed)
- [`Rc::new_zeroed_slice`](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.new_zeroed_slice)
- [`Arc::new_zeroed`](https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.new_zeroed)
- [`Arc::new_zeroed_slice`](https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.new_zeroed_slice)
- [`btree_map::Entry::insert_entry`](https://doc.rust-lang.org/stable/std/collections/btree_map/enum.Entry.html#method.insert_entry)
- [`btree_map::VacantEntry::insert_entry`](https://doc.rust-lang.org/stable/std/collections/btree_map/struct.VacantEntry.html#method.insert_entry)
- [`impl Extend<proc_macro::Group> for proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CGroup%3E-for-TokenStream)
- [`impl Extend<proc_macro::Literal> for proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CLiteral%3E-for-TokenStream)
- [`impl Extend<proc_macro::Punct> for proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CPunct%3E-for-TokenStream)
- [`impl Extend<proc_macro::Ident> for proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CIdent%3E-for-TokenStream)
These previously stable APIs are now stable in const contexts:
- [`<[_]>::rotate_left`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.rotate_left)
- [`<[_]>::rotate_right`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.rotate_right)
<a id="1.92.0-Cargo"></a>
Cargo
-----
- [Added a new chapter](https://github.com/rust-lang/cargo/issues/16119) to the Cargo book, ["Optimizing Build Performance"](https://doc.rust-lang.org/stable/cargo/guide/build-performance.html).
<a id="1.92.0-Rustdoc"></a>
Rustdoc
-----
- [If a trait item appears in rustdoc search, hide the corresponding impl items](https://github.com/rust-lang/rust/pull/145898). Previously a search for "last" would show both `Iterator::last` as well as impl methods like `std::vec::IntoIter::last`. Now these impl methods will be hidden, freeing up space for inherent methods like `BTreeSet::last`.
- [Relax rules for identifiers in search](https://github.com/rust-lang/rust/pull/147860). Previously you could only search for identifiers that were valid in rust code, now searches only need to be valid as part of an identifier. For example, you can now perform a search that starts with a digit.
<a id="1.92.0-Compatibility-Notes"></a>
Compatibility Notes
-------------------
* [Fix backtraces with `-C panic=abort` on Linux by generating unwind tables by default](https://github.com/rust-lang/rust/pull/143613). Build with `-C force-unwind-tables=no` to keep omitting unwind tables.
- As part of the larger effort refactoring compiler built-in attributes and their diagnostics, [the future-compatibility lint `invalid_macro_export_arguments` is upgraded to deny-by-default and will be reported in dependencies too.](https://github.com/rust-lang/rust/pull/143857)
- [Update the minimum external LLVM to 20](https://github.com/rust-lang/rust/pull/145071)
- [Prevent downstream `impl DerefMut for Pin<LocalType>`](https://github.com/rust-lang/rust/pull/145608)
- [Don't apply temporary lifetime extension rules to the arguments of non-extended `pin!` and formatting macros](https://github.com/rust-lang/rust/pull/145838)
Version 1.91.1 (2025-11-10)
===========================
+1 -1
View File
@@ -10,13 +10,13 @@
// 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(maybe_uninit_slice)]
#![feature(never_type)]
#![feature(rustc_attrs)]
#![feature(unwrap_infallible)]
+74 -29
View File
@@ -141,16 +141,11 @@ pub fn is_global(&self) -> bool {
/// 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.
///
/// 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
/// 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, allow_mgca_arg: bool) -> bool {
allow_mgca_arg
|| self.segments.len() == 1 && self.segments.iter().all(|seg| seg.args.is_none())
pub fn is_potential_trivial_const_arg(&self) -> bool {
self.segments.len() == 1 && self.segments.iter().all(|seg| seg.args.is_none())
}
}
@@ -1259,6 +1254,19 @@ 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>,
@@ -1372,6 +1380,15 @@ 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)
@@ -1381,6 +1398,7 @@ pub enum UnsafeSource {
pub struct AnonConst {
pub id: NodeId,
pub value: Box<Expr>,
pub mgca_disambiguation: MgcaDisambiguation,
}
/// An expression.
@@ -1399,26 +1417,20 @@ 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`.
///
/// 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.
/// 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, allow_mgca_arg: bool) -> bool {
pub fn is_potential_trivial_const_arg(&self) -> bool {
let this = self.maybe_unwrap_block();
if allow_mgca_arg {
matches!(this.kind, ExprKind::Path(..))
if let ExprKind::Path(None, path) = &this.kind
&& path.is_potential_trivial_const_arg()
{
true
} else {
if let ExprKind::Path(None, path) = &this.kind
&& path.is_potential_trivial_const_arg(allow_mgca_arg)
{
true
} else {
false
}
false
}
}
@@ -1521,11 +1533,10 @@ pub fn to_ty(&self) -> Option<Box<Ty>> {
// 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 => {
if let (Some(lhs), Some(rhs)) = (lhs.to_bound(), rhs.to_bound()) {
TyKind::TraitObject(vec![lhs, rhs], TraitObjectSyntax::None)
} else {
let (Some(lhs), Some(rhs)) = (lhs.to_bound(), rhs.to_bound()) else {
return None;
}
};
TyKind::TraitObject(vec![lhs, rhs], TraitObjectSyntax::None)
}
ExprKind::Underscore => TyKind::Infer,
@@ -1806,8 +1817,14 @@ pub enum ExprKind {
/// A use expression (`x.use`). Span is of use keyword.
Use(Box<Expr>, Span),
/// A try block (`try { ... }`).
TryBlock(Box<Block>),
/// 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>>),
/// An assignment (`a = foo()`).
/// The `Span` argument is the span of the `=` token.
@@ -2090,6 +2107,19 @@ pub struct MacroDef {
pub body: Box<DelimArgs>,
/// `true` if macro was defined with `macro_rules`.
pub macro_rules: bool,
/// If this is a macro used for externally implementable items,
/// it refers to an extern item which is its "target". This requires
/// name resolution so can't just be an attribute, so we store it in this field.
pub eii_extern_target: Option<EiiExternTarget>,
}
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
pub struct EiiExternTarget {
/// path to the extern item we're targetting
pub extern_item_path: Path,
pub impl_unsafe: bool,
pub span: Span,
}
#[derive(Clone, Encodable, Decodable, Debug, Copy, Hash, Eq, PartialEq)]
@@ -3729,6 +3759,21 @@ 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,
pub impl_safety: Safety,
pub span: Span,
pub inner_span: Span,
pub is_default: bool,
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
@@ -4095,7 +4140,7 @@ mod size_asserts {
static_assert_size!(Block, 32);
static_assert_size!(Expr, 72);
static_assert_size!(ExprKind, 40);
static_assert_size!(Fn, 184);
static_assert_size!(Fn, 192);
static_assert_size!(ForeignItem, 80);
static_assert_size!(ForeignItemKind, 16);
static_assert_size!(GenericArg, 24);
+69 -21
View File
@@ -13,7 +13,9 @@
Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path,
PathSegment, Safety,
};
use crate::token::{self, CommentKind, Delimiter, InvisibleOrigin, MetaVarKind, Token};
use crate::token::{
self, CommentKind, Delimiter, DocFragmentKind, InvisibleOrigin, MetaVarKind, Token,
};
use crate::tokenstream::{
DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenStreamIter, TokenTree,
};
@@ -179,15 +181,21 @@ fn value_str(&self) -> Option<Symbol> {
}
/// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
/// * `///doc` returns `Some(("doc", CommentKind::Line))`.
/// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
/// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
/// * `///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 `None`.
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
match &self.kind {
AttrKind::DocComment(kind, data) => Some((*data, *kind)),
AttrKind::DocComment(kind, data) => Some((*data, DocFragmentKind::Sugared(*kind))),
AttrKind::Normal(normal) if normal.item.path == sym::doc => {
normal.item.value_str().map(|s| (s, CommentKind::Line))
if let Some(value) = normal.item.value_str()
&& let Some(value_span) = normal.item.value_span()
{
Some((value, DocFragmentKind::Raw(value_span)))
} else {
None
}
}
_ => None,
}
@@ -220,6 +228,24 @@ fn doc_resolution_scope(&self) -> Option<AttrStyle> {
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 {
@@ -300,6 +326,25 @@ fn value_str(&self) -> Option<Symbol> {
}
}
/// 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 {
AttrArgs::Eq { expr, .. } => Some(expr.span),
AttrArgs::Delimited(_) | AttrArgs::Empty => None,
}
}
pub fn meta(&self, span: Span) -> Option<MetaItem> {
Some(MetaItem {
unsafety: Safety::Default,
@@ -402,20 +447,17 @@ fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItem> {
thin_vec![PathSegment::path_root(span)]
};
loop {
if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
iter.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref()
{
segments.push(PathSegment::from_ident(Ident::new(name, span)));
} else {
else {
return None;
}
if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
iter.peek()
{
iter.next();
} else {
};
segments.push(PathSegment::from_ident(Ident::new(name, span)));
let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) = iter.peek()
else {
break;
}
};
iter.next();
}
let span = span.with_hi(segments.last().unwrap().ident.span.hi());
Path { span, segments, tokens: None }
@@ -820,7 +862,7 @@ fn is_proc_macro_attr(&self) -> bool {
/// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
/// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
/// * `#[doc(...)]` returns `None`.
fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)>;
fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)>;
/// Returns outer or inner if this is a doc attribute or a sugared doc
/// comment, otherwise None.
@@ -830,6 +872,12 @@ fn is_proc_macro_attr(&self) -> bool {
/// 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
@@ -902,7 +950,7 @@ pub fn is_proc_macro_attr(&self) -> bool {
AttributeExt::is_proc_macro_attr(self)
}
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
AttributeExt::doc_str_and_comment_kind(self)
pub fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
AttributeExt::doc_str_and_fragment_kind(self)
}
}
+2 -1
View File
@@ -5,8 +5,9 @@
//! This API is completely unstable and subject to change.
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(array_windows))]
#![deny(clippy::manual_let_else)]
#![doc(test(attr(deny(warnings), allow(internal_features))))]
#![feature(array_windows)]
#![feature(associated_type_defaults)]
#![feature(box_patterns)]
#![feature(if_let_guard)]
+25 -1
View File
@@ -16,7 +16,31 @@
use crate::ast;
use crate::util::case::Case;
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
/// 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, Encodable, Decodable, Debug, HashStable_Generic)]
pub enum CommentKind {
Line,
Block,
+9 -5
View File
@@ -393,6 +393,7 @@ pub fn ctxt(&self) -> Option<FnCtxt> {
ThinVec<Pat>,
ThinVec<Box<Ty>>,
ThinVec<TyPat>,
ThinVec<EiiImpl>,
);
// This macro generates `impl Visitable` and `impl MutVisitable` that forward to `Walkable`
@@ -415,6 +416,7 @@ pub fn ctxt(&self) -> Option<FnCtxt> {
UnsafeBinderCastKind,
BinOpKind,
BlockCheckMode,
MgcaDisambiguation,
BorrowKind,
BoundAsyncness,
BoundConstness,
@@ -485,6 +487,8 @@ pub fn ctxt(&self) -> Option<FnCtxt> {
WhereEqPredicate,
WhereRegionPredicate,
YieldKind,
EiiExternTarget,
EiiImpl,
);
/// Each method of this trait is a hook to be potentially
@@ -919,13 +923,13 @@ pub fn walk_fn<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, kind: FnKind<$($lt)?
_ctxt,
// Visibility is visited as a part of the item.
_vis,
Fn { defaultness, ident, sig, generics, contract, body, define_opaque },
Fn { defaultness, ident, sig, generics, contract, body, define_opaque, eii_impls },
) => {
let FnSig { header, decl, span } = sig;
visit_visitable!($($mut)? vis,
defaultness, ident, header, generics, decl,
contract, body, span, define_opaque
)
contract, body, span, define_opaque, eii_impls
);
}
FnKind::Closure(binder, coroutine_kind, decl, body) =>
visit_visitable!($($mut)? vis, binder, coroutine_kind, decl, body),
@@ -1048,8 +1052,8 @@ pub fn walk_fn<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, kind: FnKind<$($lt)?
visit_visitable!($($mut)? vis, kind),
ExprKind::Try(subexpression) =>
visit_visitable!($($mut)? vis, subexpression),
ExprKind::TryBlock(body) =>
visit_visitable!($($mut)? vis, body),
ExprKind::TryBlock(body, optional_type) =>
visit_visitable!($($mut)? vis, body, optional_type),
ExprKind::Lit(token) =>
visit_visitable!($($mut)? vis, token),
ExprKind::IncludedBytes(bytes) =>
+13 -1
View File
@@ -23,7 +23,7 @@ pub(super) fn lower_contract(
// The order in which things are lowered is important! I.e to
// refer to variables in contract_decls from postcond/precond,
// we must lower it first!
let contract_decls = self.lower_stmts(&contract.declarations).0;
let contract_decls = self.lower_decls(contract);
match (&contract.requires, &contract.ensures) {
(Some(req), Some(ens)) => {
@@ -124,6 +124,18 @@ pub(super) fn lower_contract(
}
}
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);
+178 -6
View File
@@ -43,12 +43,15 @@
use hir::{BodyId, HirId};
use rustc_abi::ExternAbi;
use rustc_ast::*;
use rustc_attr_parsing::{AttributeParser, ShouldEmit};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::Target;
use rustc_hir::attrs::{AttributeKind, InlineAttr};
use rustc_hir::def_id::DefId;
use rustc_middle::span_bug;
use rustc_middle::ty::{Asyncness, ResolverAstLowering};
use rustc_middle::ty::{Asyncness, DelegationFnSigAttrs, ResolverAstLowering};
use rustc_span::symbol::kw;
use rustc_span::{Ident, Span, Symbol};
use rustc_span::{DUMMY_SP, Ident, Span, Symbol};
use {rustc_ast as ast, rustc_hir as hir};
use super::{GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode};
@@ -61,6 +64,41 @@ pub(crate) struct DelegationResults<'hir> {
pub generics: &'hir hir::Generics<'hir>,
}
struct AttributeAdditionInfo {
pub equals: fn(&hir::Attribute) -> bool,
pub kind: AttributeAdditionKind,
}
enum AttributeAdditionKind {
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 ATTRIBUTES_ADDITIONS: &[AttributeAdditionInfo] = &[
AttributeAdditionInfo {
equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::MustUse { .. })),
kind: AttributeAdditionKind::Inherit {
factory: |span, original_attribute| {
let reason = match original_attribute {
hir::Attribute::Parsed(AttributeKind::MustUse { reason, .. }) => *reason,
_ => None,
};
hir::Attribute::Parsed(AttributeKind::MustUse { span, reason })
},
flag: DelegationFnSigAttrs::MUST_USE,
},
},
AttributeAdditionInfo {
equals: |a| matches!(a, hir::Attribute::Parsed(AttributeKind::Inline(..))),
kind: AttributeAdditionKind::Default {
factory: |span| hir::Attribute::Parsed(AttributeKind::Inline(InlineAttr::Hint, span)),
},
},
];
impl<'hir> LoweringContext<'_, 'hir> {
fn is_method(&self, def_id: DefId, span: Span) -> bool {
match self.tcx.def_kind(def_id) {
@@ -87,6 +125,8 @@ pub(crate) fn lower_delegation(
let sig_id = self.get_delegation_sig_id(item_id, delegation.id, span, is_in_trait_impl);
match sig_id {
Ok(sig_id) => {
self.add_attributes_if_needed(span, 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);
@@ -96,7 +136,103 @@ pub(crate) fn lower_delegation(
let generics = self.lower_delegation_generics(span);
DelegationResults { body_id, sig, ident, generics }
}
Err(err) => self.generate_delegation_error(err, span),
Err(err) => self.generate_delegation_error(err, span, delegation),
}
}
fn add_attributes_if_needed(&mut self, span: Span, sig_id: DefId) {
let new_attributes = self.create_new_attributes(
ATTRIBUTES_ADDITIONS,
span,
sig_id,
self.attrs.get(&PARENT_ID),
);
if new_attributes.is_empty() {
return;
}
let new_arena_allocated_attributes = match self.attrs.get(&PARENT_ID) {
Some(existing_attrs) => self.arena.alloc_from_iter(
existing_attrs.iter().map(|a| a.clone()).chain(new_attributes.into_iter()),
),
None => self.arena.alloc_from_iter(new_attributes.into_iter()),
};
self.attrs.insert(PARENT_ID, new_arena_allocated_attributes);
}
fn create_new_attributes(
&self,
candidate_additions: &[AttributeAdditionInfo],
span: Span,
sig_id: DefId,
existing_attrs: Option<&&[hir::Attribute]>,
) -> Vec<hir::Attribute> {
let local_original_attributes = self.parse_local_original_attributes(sig_id);
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;
}
match addition_info.kind {
AttributeAdditionKind::Default { factory } => Some(factory(span)),
AttributeAdditionKind::Inherit { flag, factory } => {
let original_attribute = match sig_id.as_local() {
Some(local_id) => self
.resolver
.delegation_fn_sigs
.get(&local_id)
.is_some_and(|sig| sig.attrs_flags.contains(flag))
.then(|| {
local_original_attributes
.as_ref()
.map(|attrs| {
attrs
.iter()
.find(|base_attr| (addition_info.equals)(base_attr))
})
.flatten()
})
.flatten(),
None => self
.tcx
.get_all_attrs(sig_id)
.iter()
.find(|base_attr| (addition_info.equals)(base_attr)),
};
original_attribute.map(|a| factory(span, a))
}
}
})
.collect::<Vec<_>>()
}
fn parse_local_original_attributes(&self, sig_id: DefId) -> Option<Vec<hir::Attribute>> {
if let Some(local_id) = sig_id.as_local()
&& let Some(info) = self.resolver.delegation_fn_sigs.get(&local_id)
&& !info.to_inherit_attrs.is_empty()
{
Some(AttributeParser::parse_limited_all(
self.tcx.sess,
info.to_inherit_attrs.as_slice(),
None,
Target::Fn,
DUMMY_SP,
DUMMY_NODE_ID,
Some(self.tcx.features()),
ShouldEmit::Nothing,
))
} else {
None
}
}
@@ -192,7 +328,9 @@ fn lower_delegation_sig(
// 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.target_feature || self.tcx.def_kind(parent) == DefKind::ForeignMod {
if sig.attrs_flags.contains(DelegationFnSigAttrs::TARGET_FEATURE)
|| self.tcx.def_kind(parent) == DefKind::ForeignMod
{
hir::Safety::Unsafe
} else {
hir::Safety::Safe
@@ -404,6 +542,7 @@ fn generate_delegation_error(
&mut self,
err: ErrorGuaranteed,
span: Span,
delegation: &Delegation,
) -> DelegationResults<'hir> {
let generics = self.lower_delegation_generics(span);
@@ -418,8 +557,41 @@ fn generate_delegation_error(
let header = self.generate_header_error();
let sig = hir::FnSig { decl, header, span };
let ident = Ident::dummy();
let body_id = self.lower_body(|this| (&[], this.mk_expr(hir::ExprKind::Err(err), span)));
let ident = self.lower_ident(delegation.ident);
let body_id = self.lower_body(|this| {
let body_expr = match delegation.body.as_ref() {
Some(box block) => {
// Generates a block when we failed to resolve delegation, where a target expression is its only statement,
// thus there will be no ICEs on further stages of analysis (see #144594)
// As we generate a void function we want to convert target expression to statement to avoid additional
// errors, such as mismatched return type
let stmts = this.arena.alloc_from_iter([hir::Stmt {
hir_id: this.next_id(),
kind: rustc_hir::StmtKind::Semi(
this.arena.alloc(this.lower_target_expr(block)),
),
span,
}]);
let block = this.arena.alloc(hir::Block {
stmts,
expr: None,
hir_id: this.next_id(),
rules: hir::BlockCheckMode::DefaultBlock,
span,
targeted_by_break: false,
});
hir::ExprKind::Block(block, None)
}
None => hir::ExprKind::Err(err),
};
(&[], this.mk_expr(body_expr, span))
});
DelegationResults { ident, generics, body_id, sig }
}
+58 -22
View File
@@ -1,3 +1,4 @@
use std::mem;
use std::ops::ControlFlow;
use std::sync::Arc;
@@ -27,7 +28,9 @@
GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt,
};
use crate::errors::{InvalidLegacyConstGenericArg, UseConstGenericArg, YieldInClosure};
use crate::{AllowReturnTypeNotation, FnDeclKind, ImplTraitPosition, fluent_generated};
use crate::{
AllowReturnTypeNotation, FnDeclKind, ImplTraitPosition, TryBlockScope, fluent_generated,
};
struct WillCreateDefIdsVisitor {}
@@ -199,7 +202,9 @@ pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> {
)
})
}
ExprKind::TryBlock(body) => self.lower_expr_try_block(body),
ExprKind::TryBlock(body, opt_ty) => {
self.lower_expr_try_block(body, opt_ty.as_deref())
}
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))),
@@ -484,7 +489,11 @@ fn lower_legacy_const_generics(
arg
};
let anon_const = AnonConst { id: node_id, value: const_value };
let anon_const = AnonConst {
id: node_id,
value: const_value,
mgca_disambiguation: MgcaDisambiguation::AnonConst,
};
generic_args.push(AngleBracketedArg::Arg(GenericArg::Const(anon_const)));
} else {
real_args.push(arg);
@@ -562,9 +571,14 @@ fn lower_expr_while_in_loop_scope(
/// 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) -> hir::ExprKind<'hir> {
fn lower_expr_try_block(&mut self, body: &Block, opt_ty: Option<&Ty>) -> hir::ExprKind<'hir> {
let body_hir_id = self.lower_node_id(body.id);
self.with_catch_scope(body_hir_id, |this| {
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| {
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
@@ -598,8 +612,16 @@ fn lower_expr_try_block(&mut self, body: &Block) -> hir::ExprKind<'hir> {
ok_wrapped_span,
));
hir::ExprKind::Block(this.arena.alloc(block), None)
})
this.arena.alloc(block)
});
if let Some(ty) = opt_ty {
let ty = self.lower_ty(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)
}
}
fn wrap_in_try_constructor(
@@ -1617,10 +1639,14 @@ fn lower_jump_destination(&mut self, id: NodeId, opt_label: Option<Label>) -> hi
}
}
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);
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);
let result = f(self);
self.catch_scope = old_scope;
self.try_block_scope = old_scope;
result
}
@@ -1978,18 +2004,25 @@ fn lower_expr_try(&mut self, span: Span, sub_expr: &Expr) -> hir::ExprKind<'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(
if self.catch_scope.is_some() {
hir::LangItem::ResidualIntoTryType
} else {
hir::LangItem::TryTraitFromResidual
},
constructor_item,
try_span,
self.arena.alloc(residual_expr),
unstable_span,
);
let ret_expr = if let Some(catch_id) = self.catch_scope {
let target_id = Ok(catch_id);
let ret_expr = if target_id.is_ok() {
self.arena.alloc(self.expr(
try_span,
hir::ExprKind::Break(
@@ -2044,11 +2077,14 @@ fn lower_expr_yeet(&mut self, span: Span, sub_expr: Option<&Expr>) -> hir::ExprK
yeeted_span,
);
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))
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)),
}
}
+109 -4
View File
@@ -2,7 +2,7 @@
use rustc_ast::visit::AssocCtxt;
use rustc_ast::*;
use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::attrs::{AttributeKind, EiiDecl};
use rustc_hir::def::{DefKind, PerNS, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
use rustc_hir::{
@@ -11,6 +11,7 @@
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};
@@ -133,10 +134,92 @@ pub(super) fn lower_mod(
}
}
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()
.flat_map(
|EiiImpl {
node_id,
eii_macro_path,
impl_safety,
span,
inner_span,
is_default,
}| {
self.lower_path_simple_eii(*node_id, eii_macro_path).map(|did| {
hir::attrs::EiiImpl {
eii_macro: did,
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,
}
})
},
)
.collect(),
))]
}
ItemKind::MacroDef(
_,
MacroDef {
eii_extern_target: Some(EiiExternTarget { extern_item_path, impl_unsafe, span }),
..
},
) => self
.lower_path_simple_eii(id, extern_item_path)
.map(|did| {
vec![hir::Attribute::Parsed(AttributeKind::EiiExternTarget(EiiDecl {
eii_extern_target: did,
impl_unsafe: *impl_unsafe,
span: self.lower_span(*span),
}))]
})
.unwrap_or_default(),
ItemKind::ExternCrate(..)
| ItemKind::Use(..)
| ItemKind::Static(..)
| ItemKind::Const(..)
| 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 attrs = self.lower_attrs(hir_id, &i.attrs, i.span, Target::from_ast_item(i));
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 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(),
@@ -144,6 +227,10 @@ fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> {
vis_span,
span: self.lower_span(i.span),
has_delayed_lints: !self.delayed_lints.is_empty(),
eii: find_attr!(
attrs,
AttributeKind::EiiImpls(..) | AttributeKind::EiiExternTarget(..)
),
};
self.arena.alloc(item)
}
@@ -435,7 +522,7 @@ fn lower_item_kind(
);
hir::ItemKind::TraitAlias(constness, ident, generics, bounds)
}
ItemKind::MacroDef(ident, MacroDef { body, macro_rules }) => {
ItemKind::MacroDef(ident, MacroDef { body, macro_rules, eii_extern_target: _ }) => {
let ident = self.lower_ident(*ident);
let body = Box::new(self.lower_delim_args(body));
let def_id = self.local_def_id(id);
@@ -446,7 +533,11 @@ fn lower_item_kind(
def_kind.descr(def_id.to_def_id())
);
};
let macro_def = self.arena.alloc(ast::MacroDef { body, macro_rules: *macro_rules });
let macro_def = self.arena.alloc(ast::MacroDef {
body,
macro_rules: *macro_rules,
eii_extern_target: None,
});
hir::ItemKind::Macro(ident, macro_def, macro_kinds)
}
ItemKind::Delegation(box delegation) => {
@@ -465,6 +556,16 @@ fn lower_item_kind(
}
}
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,
@@ -573,6 +674,10 @@ fn lower_use_tree(
vis_span,
span: this.lower_span(use_tree.span),
has_delayed_lints: !this.delayed_lints.is_empty(),
eii: find_attr!(
attrs,
AttributeKind::EiiImpls(..) | AttributeKind::EiiExternTarget(..)
),
};
hir::OwnerNode::Item(this.arena.alloc(item))
});
+103 -19
View File
@@ -35,6 +35,7 @@
#![feature(if_let_guard)]
// tidy-alphabetical-end
use std::mem;
use std::sync::Arc;
use rustc_ast::node_id::NodeMap;
@@ -117,7 +118,7 @@ struct LoweringContext<'a, 'hir> {
/// outside of an `async fn`.
current_item: Option<Span>,
catch_scope: Option<HirId>,
try_block_scope: TryBlockScope,
loop_scope: Option<HirId>,
is_in_loop_condition: bool,
is_in_dyn_type: bool,
@@ -173,7 +174,7 @@ fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self {
trait_map: Default::default(),
// Lowering state.
catch_scope: None,
try_block_scope: TryBlockScope::Function,
loop_scope: None,
is_in_loop_condition: false,
is_in_dyn_type: false,
@@ -416,6 +417,18 @@ 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,
@@ -936,10 +949,10 @@ fn with_new_scopes<T>(&mut self, scope_span: Span, f: impl FnOnce(&mut Self) ->
let old_contract = self.contract_ensures.take();
let catch_scope = self.catch_scope.take();
let try_block_scope = mem::replace(&mut self.try_block_scope, TryBlockScope::Function);
let loop_scope = self.loop_scope.take();
let ret = f(self);
self.catch_scope = catch_scope;
self.try_block_scope = try_block_scope;
self.loop_scope = loop_scope;
self.contract_ensures = old_contract;
@@ -958,11 +971,23 @@ fn lower_attrs(
target_span: Span,
target: Target,
) -> &'hir [hir::Attribute] {
if attrs.is_empty() {
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() {
&[]
} else {
let lowered_attrs =
let mut 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);
@@ -1206,7 +1231,7 @@ fn lower_generic_arg(
.and_then(|partial_res| partial_res.full_res())
{
if !res.matches_ns(Namespace::TypeNS)
&& path.is_potential_trivial_const_arg(false)
&& path.is_potential_trivial_const_arg()
{
debug!(
"lower_generic_arg: Lowering type argument as const argument: {:?}",
@@ -1670,7 +1695,7 @@ fn lower_fn_decl(
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, fn_span)
self.lower_coroutine_fn_ret_ty(&decl.output, fn_def_id, coro, kind)
}
None => match &decl.output {
FnRetTy::Ty(ty) => {
@@ -1755,9 +1780,8 @@ fn lower_coroutine_fn_ret_ty(
fn_def_id: LocalDefId,
coro: CoroutineKind,
fn_kind: FnDeclKind,
fn_span: Span,
) -> hir::FnRetTy<'hir> {
let span = self.lower_span(fn_span);
let span = self.lower_span(output.span());
let (opaque_ty_node_id, allowed_features) = match coro {
CoroutineKind::Async { return_impl_trait_id, .. } => (return_impl_trait_id, None),
@@ -2275,11 +2299,9 @@ fn lower_const_path_to_const_arg(
) -> &'hir hir::ConstArg<'hir> {
let tcx = self.tcx;
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 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 qpath = self.lower_qpath(
ty_id,
&None,
@@ -2358,6 +2380,53 @@ fn lower_const_item_rhs(
}
}
#[instrument(level = "debug", skip(self), ret)]
fn lower_expr_to_const_arg_direct(&mut self, expr: &Expr) -> hir::ConstArg<'hir> {
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(expr.span, e.emit()) }
};
match &expr.kind {
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) }
}
ExprKind::Underscore => ConstArg {
hir_id: self.lower_node_id(expr.id),
kind: hir::ConstArgKind::Infer(expr.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(..)
)
{
return self.lower_expr_to_const_arg_direct(expr);
}
overly_complex_const(self)
}
_ => overly_complex_const(self),
}
}
/// See [`hir::ConstArg`] for when to use this function vs
/// [`Self::lower_anon_const_to_anon_const`].
fn lower_anon_const_to_const_arg(&mut self, anon: &AnonConst) -> &'hir hir::ConstArg<'hir> {
@@ -2367,6 +2436,22 @@ fn lower_anon_const_to_const_arg(&mut self, anon: &AnonConst) -> &'hir hir::Cons
#[instrument(level = "debug", skip(self))]
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);
ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Anon(lowered_anon) }
}
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
@@ -2378,12 +2463,12 @@ fn lower_anon_const_to_const_arg_direct(&mut self, anon: &AnonConst) -> hir::Con
} 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(tcx.features().min_generic_const_args())
&& (tcx.features().min_generic_const_args()
|| matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _))))
&& path.is_potential_trivial_const_arg()
&& matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _)))
{
let qpath = self.lower_qpath(
expr.id,
@@ -2391,7 +2476,6 @@ fn lower_anon_const_to_const_arg_direct(&mut self, anon: &AnonConst) -> hir::Con
path,
ParamMode::Explicit,
AllowReturnTypeNotation::No,
// FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
+2
View File
@@ -80,6 +80,8 @@ ast_passes_c_variadic_must_be_unsafe =
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
@@ -33,7 +33,7 @@
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,
PATTERNS_IN_FNS_WITHOUT_BODY, UNUSED_VISIBILITIES,
};
use rustc_session::parse::feature_err;
use rustc_span::{Ident, Span, kw, sym};
@@ -710,6 +710,14 @@ fn check_c_variadic_type(&self, fk: FnKind<'a>, attrs: &'a AttrVec) {
match fn_ctxt {
FnCtxt::Foreign => return,
FnCtxt::Free | FnCtxt::Assoc(_) => {
if !self.sess.target.arch.supports_c_variadic_definitions() {
self.dcx().emit_err(errors::CVariadicNotSupported {
variadic_span: variadic_param.span,
target: &*self.sess.target.llvm_target,
});
return;
}
match sig.header.ext {
Extern::Implicit(_) => {
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
@@ -1067,7 +1075,7 @@ fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericPara
impl<'a> Visitor<'a> for AstValidator<'a> {
fn visit_attribute(&mut self, attr: &Attribute) {
validate_attr::check_attr(&self.sess.psess, attr, self.lint_node_id);
validate_attr::check_attr(&self.sess.psess, attr);
}
fn visit_ty(&mut self, ty: &'a Ty) {
@@ -1171,11 +1179,16 @@ fn visit_item(&mut self, item: &'a Item) {
contract: _,
body,
define_opaque: _,
eii_impls,
},
) => {
self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
self.check_defaultness(item.span, *defaultness);
for EiiImpl { eii_macro_path, .. } in eii_impls {
self.visit_path(eii_macro_path);
}
let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic));
if body.is_none() && !is_intrinsic && !self.is_sdylib_interface {
self.dcx().emit_err(errors::FnWithoutBody {
@@ -1331,7 +1344,7 @@ fn visit_item(&mut self, item: &'a Item) {
}
});
}
ItemKind::Const(box ConstItem { defaultness, rhs, .. }) => {
ItemKind::Const(box ConstItem { defaultness, ident, rhs, .. }) => {
self.check_defaultness(item.span, *defaultness);
if rhs.is_none() {
self.dcx().emit_err(errors::ConstWithoutBody {
@@ -1339,6 +1352,18 @@ fn visit_item(&mut self, item: &'a Item) {
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, .. }) => {
+8
View File
@@ -733,6 +733,14 @@ pub(crate) struct CoroutineAndCVariadic {
pub variadic_span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_passes_c_variadic_not_supported)]
pub(crate) struct CVariadicNotSupported<'a> {
#[primary_span]
pub variadic_span: Span,
pub target: &'a str,
}
#[derive(Diagnostic)]
#[diag(ast_passes_pattern_in_foreign, code = E0130)]
// FIXME: deduplicate with rustc_lint (`BuiltinLintDiag::PatternsInFnsWithoutBody`)
+15 -11
View File
@@ -301,17 +301,12 @@ fn visit_ty(&mut self, ty: &'a ast::Ty) {
visit::walk_ty(self, ty)
}
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_pred.bound_generic_params);
}
_ => {}
}
fn visit_where_predicate_kind(&mut self, kind: &'a ast::WherePredicateKind) {
if let ast::WherePredicateKind::BoundPredicate(bound) = kind {
// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`).
self.check_late_bound_lifetime_defs(&bound.bound_generic_params);
}
visit::walk_generics(self, g);
visit::walk_where_predicate_kind(self, kind);
}
fn visit_fn_ret_ty(&mut self, ret_ty: &'a ast::FnRetTy) {
@@ -339,9 +334,17 @@ fn visit_generic_args(&mut self, args: &'a ast::GenericArgs) {
fn visit_expr(&mut self, e: &'a ast::Expr) {
match e.kind {
ast::ExprKind::TryBlock(_) => {
ast::ExprKind::TryBlock(_, None) => {
gate!(&self, try_blocks, e.span, "`try` expression is experimental");
}
ast::ExprKind::TryBlock(_, Some(_)) => {
gate!(
&self,
try_blocks_heterogeneous,
e.span,
"`try bikeshed` expression is experimental"
);
}
ast::ExprKind::Lit(token::Lit {
kind: token::LitKind::Float | token::LitKind::Integer,
suffix,
@@ -514,6 +517,7 @@ macro_rules! gate_all {
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");
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");
+49 -9
View File
@@ -10,7 +10,7 @@
use std::sync::Arc;
use rustc_ast::attr::AttrIdGenerator;
use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
use rustc_ast::token::{self, CommentKind, Delimiter, DocFragmentKind, Token, TokenKind};
use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
use rustc_ast::util::classify;
use rustc_ast::util::comments::{Comment, CommentStyle};
@@ -381,15 +381,24 @@ fn is_punct(tt: &TokenTree) -> bool {
}
pub fn doc_comment_to_string(
comment_kind: CommentKind,
fragment_kind: DocFragmentKind,
attr_style: ast::AttrStyle,
data: Symbol,
) -> String {
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}*/"),
match fragment_kind {
DocFragmentKind::Sugared(comment_kind) => 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(),
)
}
}
}
@@ -665,7 +674,11 @@ fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) ->
self.word("]");
}
ast::AttrKind::DocComment(comment_kind, data) => {
self.word(doc_comment_to_string(*comment_kind, attr.style, *data));
self.word(doc_comment_to_string(
DocFragmentKind::Sugared(*comment_kind),
attr.style,
*data,
));
self.hardbreak()
}
}
@@ -852,6 +865,17 @@ fn print_mac_def(
sp: Span,
print_visibility: impl FnOnce(&mut Self),
) {
if let Some(eii_extern_target) = &macro_def.eii_extern_target {
self.word("#[eii_extern_target(");
self.print_path(&eii_extern_target.extern_item_path, false, 0);
if eii_extern_target.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 {
@@ -1029,7 +1053,8 @@ fn token_kind_to_string_ext(
/* Other */
token::DocComment(comment_kind, attr_style, data) => {
doc_comment_to_string(comment_kind, attr_style, data).into()
doc_comment_to_string(DocFragmentKind::Sugared(comment_kind), attr_style, data)
.into()
}
token::Eof => "<eof>".into(),
}
@@ -2148,6 +2173,15 @@ fn print_meta_list_item(&mut self, item: &ast::MetaItemInner) {
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) => {
@@ -2163,6 +2197,12 @@ fn print_meta_item(&mut self, item: &ast::MetaItem) {
self.pclose();
}
}
match item.unsafety {
ast::Safety::Unsafe(_) => self.pclose(),
ast::Safety::Default | ast::Safety::Safe(_) => {}
}
self.end(ib);
}
@@ -818,10 +818,15 @@ pub(super) fn print_expr_outer_attr_style(
);
self.word("?")
}
ast::ExprKind::TryBlock(blk) => {
ast::ExprKind::TryBlock(blk, opt_ty) => {
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) => {
@@ -1,6 +1,6 @@
use ast::StaticItem;
use itertools::{Itertools, Position};
use rustc_ast::{self as ast, ModKind, TraitAlias};
use rustc_ast::{self as ast, EiiImpl, ModKind, Safety, TraitAlias};
use rustc_span::Ident;
use crate::pp::BoxMarker;
@@ -671,10 +671,25 @@ fn print_delegation(
}
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 } = func;
let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque, eii_impls } =
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);
+20 -41
View File
@@ -6,35 +6,38 @@ attr_parsing_bundle_needs_static =
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_doc_alias_bad_char =
{$char_} character isn't allowed in {$attr_str}
attr_parsing_doc_alias_empty =
{$attr_str} attribute cannot have empty value
attr_parsing_doc_alias_malformed =
doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
attr_parsing_doc_alias_start_end =
{$attr_str} cannot start or end with ' '
attr_parsing_doc_attribute_not_attribute =
nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = "...")]`
.help = only existing builtin attributes are allowed in core/std
attr_parsing_doc_keyword_not_keyword =
nonexistent keyword `{$keyword}` used in `#[doc(keyword = "...")]`
.help = only existing keywords are allowed in core/std
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
@@ -99,6 +102,7 @@ attr_parsing_invalid_link_modifier =
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
.label = {$descr}s are not allowed here
attr_parsing_invalid_predicate =
invalid predicate `{$predicate}`
@@ -119,19 +123,9 @@ attr_parsing_invalid_repr_hint_no_value =
attr_parsing_invalid_since =
'since' must be a Rust version number, such as "1.31.0"
attr_parsing_invalid_style = {$is_used_as_inner ->
[false] crate-level attribute should be an inner attribute: add an exclamation mark: `#![{$name}]`
*[other] the `#![{$name}]` attribute can only be used at the crate root
}
.note = This attribute does not have an `!`, which means it is applied to this {$target}
attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target}
.help = `#[{$name}]` can {$only}be applied to {$applied}
.suggestion = remove the attribute
attr_parsing_invalid_target_lint = `#[{$name}]` attribute cannot be used on {$target}
.warn = {-attr_parsing_previously_accepted}
.help = `#[{$name}]` can {$only}be applied to {$applied}
.suggestion = remove the attribute
attr_parsing_limit_invalid =
`limit` must be a non-negative integer
@@ -241,28 +235,13 @@ attr_parsing_unstable_cfg_target_compact =
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
@@ -17,9 +17,9 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
]);
const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]);
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0])
.into_iter()
@@ -39,9 +39,9 @@ impl<S: Stage> CombineAttributeParser<S> for UnstableFeatureBoundParser {
]);
const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]);
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
if !cx.features().staged_api() {
cx.emit_err(session_diagnostics::StabilityOutsideStd { span: cx.attr_span });
@@ -67,17 +67,17 @@ impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
]);
const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]);
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c {
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
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();
@@ -1,15 +1,19 @@
use std::convert::identity;
use rustc_ast::token::Delimiter;
use rustc_ast::tokenstream::DelimSpan;
use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, NodeId, ast, token};
use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, ast, token};
use rustc_errors::{Applicability, PResult};
use rustc_feature::{AttrSuggestionStyle, AttributeTemplate, Features, template};
use rustc_feature::{
AttrSuggestionStyle, AttributeTemplate, Features, GatedCfg, find_gated_cfg, template,
};
use rustc_hir::attrs::CfgEntry;
use rustc_hir::lints::AttributeLintKind;
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};
@@ -21,10 +25,7 @@
AttributeParseError, AttributeParseErrorReason, CfgAttrBadDelim, MetaBadDelimSugg,
ParsedDescription,
};
use crate::{
AttributeParser, CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics,
try_gate_cfg,
};
use crate::{AttributeParser, fluent_generated, parse_version, session_diagnostics};
pub const CFG_TEMPLATE: AttributeTemplate = template!(
List: &["predicate"],
@@ -36,9 +37,9 @@
"https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute"
);
pub fn parse_cfg<'c, S: Stage>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
pub fn parse_cfg<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> Option<CfgEntry> {
let ArgParser::List(list) = args else {
cx.expected_list(cx.attr_span);
@@ -53,7 +54,7 @@ pub fn parse_cfg<'c, 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() {
@@ -99,7 +100,7 @@ pub fn parse_cfg_entry<S: Stage>(
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 +132,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()
@@ -172,7 +173,7 @@ fn parse_cfg_entry_target<S: Stage>(
Ok(CfgEntry::All(result, list.span))
}
fn parse_name_value<S: Stage>(
pub(crate) fn parse_name_value<S: Stage>(
name: Symbol,
name_span: Span,
value: Option<&NameValueParser>,
@@ -193,43 +194,46 @@ fn parse_name_value<S: Stage>(
}
};
Ok(CfgEntry::NameValue { name, name_span, value, span })
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 })
}
pub fn eval_config_entry(
sess: &Session,
cfg_entry: &CfgEntry,
id: NodeId,
emit_lints: ShouldEmit,
) -> EvalConfigResult {
pub fn eval_config_entry(sess: &Session, cfg_entry: &CfgEntry) -> EvalConfigResult {
match cfg_entry {
CfgEntry::All(subs, ..) => {
let mut all = None;
for sub in subs {
let res = eval_config_entry(sess, sub, id, emit_lints);
// We cannot short-circuit because `eval_config_entry` emits some lints
let res = eval_config_entry(sess, sub);
if !res.as_bool() {
all.get_or_insert(res);
return res;
}
}
all.unwrap_or_else(|| EvalConfigResult::True)
EvalConfigResult::True
}
CfgEntry::Any(subs, span) => {
let mut any = None;
for sub in subs {
let res = eval_config_entry(sess, sub, id, emit_lints);
// We cannot short-circuit because `eval_config_entry` emits some lints
let res = eval_config_entry(sess, sub);
if res.as_bool() {
any.get_or_insert(res);
return res;
}
}
any.unwrap_or_else(|| EvalConfigResult::False {
reason: cfg_entry.clone(),
reason_span: *span,
})
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span }
}
CfgEntry::Not(sub, span) => {
if eval_config_entry(sess, sub, id, emit_lints).as_bool() {
if eval_config_entry(sess, sub).as_bool() {
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span }
} else {
EvalConfigResult::True
@@ -242,32 +246,8 @@ pub fn eval_config_entry(
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span }
}
}
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))) {
CfgEntry::NameValue { name, value, span } => {
if sess.psess.config.contains(&(*name, *value)) {
EvalConfigResult::True
} else {
EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span }
@@ -353,7 +333,7 @@ pub fn parse_cfg_attr(
span,
attr_span: cfg_attr.span,
template: CFG_ATTR_TEMPLATE,
path: AttrPath::from_ast(&cfg_attr.get_normal_item().path),
path: AttrPath::from_ast(&cfg_attr.get_normal_item().path, identity),
description: ParsedDescription::Attribute,
reason,
suggestions: CFG_ATTR_TEMPLATE
@@ -398,6 +378,7 @@ fn parse_cfg_attr_internal<'a>(
.into_boxed_slice(),
span: attribute.span,
},
Some(attribute.get_normal_item().unsafety),
ParsedDescription::Attribute,
pred_span,
CRATE_NODE_ID,
@@ -431,3 +412,19 @@ 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);
}
}
#[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();
}
}
@@ -1,250 +0,0 @@
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,
})
}
}
}
@@ -63,6 +63,7 @@ pub fn parse_cfg_select(
segments: vec![Ident::from_str("cfg_select")].into_boxed_slice(),
span: cfg_span,
},
None,
ParsedDescription::Macro,
cfg_span,
lint_node_id,
@@ -23,7 +23,7 @@ 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);
return None;
@@ -84,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;
@@ -135,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;
@@ -164,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;
@@ -196,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;
@@ -472,10 +472,10 @@ fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
}
}
fn parse_tf_attribute<'c, S: Stage>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = (Symbol, Span)> + 'c {
fn parse_tf_attribute<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = (Symbol, Span)> {
let mut features = Vec::new();
let ArgParser::List(list) = args else {
cx.expected_list(cx.attr_span);
@@ -529,10 +529,10 @@ impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
};
const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c {
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
parse_tf_attribute(cx, args)
}
@@ -567,10 +567,10 @@ impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
Allow(Target::Method(MethodKind::TraitImpl)),
]);
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c {
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
parse_tf_attribute(cx, args)
}
}
@@ -599,7 +599,7 @@ 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);
return None;
@@ -699,3 +699,12 @@ 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 EiiExternItemParser;
impl<S: Stage> NoArgsAttributeParser<S> for EiiExternItemParser {
const PATH: &[Symbol] = &[sym::rustc_eii_extern_item];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::EiiExternItem;
}
@@ -11,7 +11,7 @@ impl<S: Stage> SingleAttributeParser<S> for CrateNameParser {
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
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;
@@ -35,7 +35,7 @@ impl<S: Stage> SingleAttributeParser<S> for RecursionLimitParser {
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute");
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel;
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
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;
@@ -58,7 +58,7 @@ impl<S: Stage> SingleAttributeParser<S> for MoveSizeLimitParser {
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
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;
@@ -81,7 +81,7 @@ impl<S: Stage> SingleAttributeParser<S> for TypeLengthLimitParser {
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
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;
@@ -104,7 +104,7 @@ impl<S: Stage> SingleAttributeParser<S> for PatternComplexityLimitParser {
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
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;
@@ -154,7 +154,7 @@ impl<S: Stage> SingleAttributeParser<S> for WindowsSubsystemParser {
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),
@@ -16,10 +16,10 @@ impl<S: Stage> CombineAttributeParser<S> for DebuggerViualizerParser {
type Item = DebugVisualizer;
const CONVERT: ConvertFn<Self::Item> = |v, _| AttributeKind::DebuggerVisualizer(v);
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c {
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let Some(l) = args.list() else {
cx.expected_list(args.span().unwrap_or(cx.attr_span));
return None;
@@ -12,7 +12,7 @@ fn get<S: Stage>(
cx: &AcceptContext<'_, '_, S>,
name: Symbol,
param_span: Span,
arg: &ArgParser<'_>,
arg: &ArgParser,
item: &Option<Symbol>,
) -> Option<Symbol> {
if item.is_some() {
@@ -68,7 +68,7 @@ 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;
@@ -0,0 +1,627 @@
use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit};
use rustc_feature::template;
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, 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
}
fn parse_keyword_and_attribute<S, F>(
cx: &mut AcceptContext<'_, '_, S>,
path: &OwnedPathParser,
args: &ArgParser,
attr_value: &mut Option<(Symbol, Span)>,
callback: F,
) where
S: Stage,
F: FnOnce(&mut AcceptContext<'_, '_, S>, Symbol, Span) -> bool,
{
let Some(nv) = args.name_value() else {
cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym());
return;
};
let Some(value) = nv.value_as_str() else {
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return;
};
if !callback(cx, value, nv.value_span) {
return;
}
if attr_value.is_some() {
cx.duplicate_key(path.span(), path.word_sym().unwrap());
return;
}
*attr_value = Some((value, path.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() {
cx.expected_no_args(span);
return;
}
if self.attribute.no_crate_inject.is_some() {
cx.duplicate_key(path.span(), sym::no_crate_inject);
return;
}
self.attribute.no_crate_inject = Some(path.span())
}
Some(sym::attr) => {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_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 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() {
cx.expected_no_args(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 {
cx.expected_identifier(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() {
cx.expected_no_args(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! string_arg {
($ident: ident) => {{
let Some(nv) = args.name_value() else {
cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym());
return;
};
let Some(s) = nv.value_as_str() else {
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
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!(html_favicon_url),
Some(sym::html_logo_url) => string_arg!(html_logo_url),
Some(sym::html_no_source) => no_args!(html_no_source),
Some(sym::html_playground_url) => string_arg!(html_playground_url),
Some(sym::html_root_url) => string_arg!(html_root_url),
Some(sym::issue_tracker_base_url) => string_arg!(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,
check_keyword,
),
Some(sym::attribute) => parse_keyword_and_attribute(
cx,
path,
args,
&mut self.attribute.attribute,
check_attribute,
),
Some(sym::fake_variadic) => no_args!(fake_variadic),
Some(sym::search_unbox) => no_args!(search_unbox),
Some(sym::rust_logo) => no_args!(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) => {
cx.unexpected_literal(lit.span);
}
MetaItemOrLitParser::Err(..) => {
// already had an error here, move on.
}
}
}
}
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::ILL_FORMED_ATTRIBUTE_INPUT,
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) => {
cx.expected_name_value(lit.span, None);
}
MetaItemOrLitParser::Err(..) => {
// already had an error here, move on.
}
}
}
}
ArgParser::NameValue(nv) => {
if nv.value_as_str().is_none() {
cx.expected_string_literal(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
}
}
}
@@ -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> {
fn convert(_: &mut AcceptContext<'_, '_, S>, _: &ArgParser) -> Option<AttributeKind> {
Some(AttributeKind::Dummy)
}
}
@@ -3,6 +3,7 @@
// 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::*;
@@ -33,7 +34,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) => {
@@ -56,9 +57,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<At
}
}
ArgParser::NameValue(_) => {
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
return None;
}
}
@@ -78,7 +77,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) => {
@@ -2,6 +2,7 @@
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};
@@ -32,7 +33,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;
@@ -61,19 +62,17 @@ 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<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c {
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let items = match args {
ArgParser::List(list) => list,
// This is an edgecase added because making this a hard error would break too many crates
// Specifically `#[link = "dl"]` is accepted with a FCW
// For more information, see https://github.com/rust-lang/rust/pull/143193
ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => {
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
return None;
}
_ => {
@@ -243,7 +242,7 @@ fn extend<'c>(
impl LinkParser {
fn parse_link_name<S: Stage>(
item: &MetaItemParser<'_>,
item: &MetaItemParser,
name: &mut Option<(Symbol, Span)>,
cx: &mut AcceptContext<'_, '_, S>,
) -> bool {
@@ -268,7 +267,7 @@ fn parse_link_name<S: Stage>(
}
fn parse_link_kind<S: Stage>(
item: &MetaItemParser<'_>,
item: &MetaItemParser,
kind: &mut Option<NativeLibKind>,
cx: &mut AcceptContext<'_, '_, S>,
sess: &Session,
@@ -348,7 +347,7 @@ fn parse_link_kind<S: Stage>(
}
fn parse_link_modifiers<S: Stage>(
item: &MetaItemParser<'_>,
item: &MetaItemParser,
modifiers: &mut Option<(Symbol, Span)>,
cx: &mut AcceptContext<'_, '_, S>,
) -> bool {
@@ -369,7 +368,7 @@ fn parse_link_modifiers<S: Stage>(
}
fn parse_link_cfg<S: Stage>(
item: &MetaItemParser<'_>,
item: &MetaItemParser,
cfg: &mut Option<CfgEntry>,
cx: &mut AcceptContext<'_, '_, S>,
sess: &Session,
@@ -401,7 +400,7 @@ fn parse_link_cfg<S: Stage>(
}
fn parse_link_wasm_import_module<S: Stage>(
item: &MetaItemParser<'_>,
item: &MetaItemParser,
wasm_import_module: &mut Option<(Symbol, Span)>,
cx: &mut AcceptContext<'_, '_, S>,
) -> bool {
@@ -422,7 +421,7 @@ fn parse_link_wasm_import_module<S: Stage>(
}
fn parse_link_import_name_type<S: Stage>(
item: &MetaItemParser<'_>,
item: &MetaItemParser,
import_name_type: &mut Option<(PeImportNameType, Span)>,
cx: &mut AcceptContext<'_, '_, S>,
) -> bool {
@@ -479,7 +478,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;
@@ -552,7 +551,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
@@ -608,7 +607,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;
@@ -1,5 +1,6 @@
use rustc_errors::DiagArgValue;
use rustc_hir::attrs::MacroUseArgs;
use rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS;
use super::prelude::*;
use crate::session_diagnostics::IllFormedAttributeInputLint;
@@ -147,28 +148,18 @@ 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 {
let span = cx.attr_span;
let suggestions = cx.suggestions();
cx.emit_lint(
AttributeLintKind::InvalidMacroExportArguments { suggestions },
span,
);
cx.warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS);
return None;
};
match l.meta_item().and_then(|i| i.path().word_sym()) {
Some(sym::local_inner_macros) => true,
_ => {
let span = cx.attr_span;
let suggestions = cx.suggestions();
cx.emit_lint(
AttributeLintKind::InvalidMacroExportArguments { suggestions },
span,
);
cx.warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS);
return None;
}
}
@@ -32,13 +32,13 @@
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 codegen_attrs;
pub(crate) mod confusables;
pub(crate) mod crate_level;
pub(crate) mod debugger;
pub(crate) mod deprecation;
pub(crate) mod doc;
pub(crate) mod dummy;
pub(crate) mod inline;
pub(crate) mod link_attrs;
@@ -61,7 +61,7 @@
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.
@@ -132,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`].
@@ -281,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);
}
@@ -314,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<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c;
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item>;
}
/// Use in combination with [`CombineAttributeParser`].
@@ -29,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 {
@@ -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;
@@ -4,8 +4,6 @@
#[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};
@@ -30,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"),
@@ -49,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 })
}
@@ -57,7 +57,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<At
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 {
@@ -25,7 +25,7 @@ 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);
return None;
@@ -70,7 +70,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<At
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,
@@ -26,10 +26,10 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
"https://doc.rust-lang.org/reference/type-layout.html#representations"
);
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = Self::Item> + 'c {
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let mut reprs = Vec::new();
let Some(list) = args.list() else {
@@ -98,10 +98,7 @@ 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
@@ -192,7 +189,7 @@ enum AlignKind {
fn parse_repr_align<S: Stage>(
cx: &AcceptContext<'_, '_, S>,
list: &MetaItemListParser<'_>,
list: &MetaItemListParser,
param_span: Span,
align_kind: AlignKind,
) -> Option<ReprAttr> {
@@ -278,11 +275,7 @@ impl AlignParser {
const PATH: &'static [Symbol] = &[sym::rustc_align];
const TEMPLATE: AttributeTemplate = template!(List: &["<alignment in bytes>"]);
fn parse<'c, S: Stage>(
&mut self,
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) {
fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
match args {
ArgParser::NoArgs | ArgParser::NameValue(_) => {
cx.expected_list(cx.attr_span);
@@ -339,11 +332,7 @@ impl AlignStaticParser {
const PATH: &'static [Symbol] = &[sym::rustc_align_static];
const TEMPLATE: AttributeTemplate = AlignParser::TEMPLATE;
fn parse<'c, S: Stage>(
&mut self,
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) {
fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
self.0.parse(cx, args)
}
}
@@ -19,7 +19,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStartPars
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
const TEMPLATE: AttributeTemplate = template!(List: &["start"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
parse_single_integer(cx, args)
.map(|n| AttributeKind::RustcLayoutScalarValidRangeStart(Box::new(n), cx.attr_span))
}
@@ -34,7 +34,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeEndParser
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
const TEMPLATE: AttributeTemplate = template!(List: &["end"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
parse_single_integer(cx, args)
.map(|n| AttributeKind::RustcLayoutScalarValidRangeEnd(Box::new(n), cx.attr_span))
}
@@ -49,7 +49,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcObjectLifetimeDefaultParser {
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
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);
return None;
@@ -68,7 +68,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcSimdMonomorphizeLaneLimitParser
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
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;
@@ -8,7 +8,7 @@
use super::prelude::*;
use super::util::parse_version;
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
use crate::session_diagnostics::{self};
macro_rules! reject_outside_std {
($cx: ident) => {
@@ -267,7 +267,7 @@ fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind>
/// `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<()> {
@@ -289,7 +289,7 @@ 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;
@@ -302,12 +302,7 @@ pub(crate) fn parse_stability<S: Stage>(
for param in list.mixed() {
let param_span = param.span();
let Some(param) = param.meta_item() else {
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),
});
cx.unexpected_literal(param.span());
return None;
};
@@ -365,7 +360,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;
@@ -382,12 +377,7 @@ pub(crate) fn parse_unstability<S: Stage>(
for param in list.mixed() {
let Some(param) = param.meta_item() else {
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()),
});
cx.unexpected_literal(param.span());
return None;
};
@@ -1,3 +1,5 @@
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
use super::prelude::*;
pub(crate) struct IgnoreParser;
@@ -13,27 +15,20 @@ 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 {
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(
AttributeLintKind::IllFormedAttributeInput { suggestions },
span,
);
cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
return None;
};
Some(str_value)
}
ArgParser::List(_) => {
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
return None;
}
},
@@ -54,7 +49,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 {
@@ -18,7 +18,7 @@ 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 {
@@ -17,7 +17,7 @@ impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
const TEMPLATE: AttributeTemplate =
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;
@@ -5,7 +5,7 @@
use rustc_feature::is_builtin_attr_name;
use rustc_hir::RustcVersion;
use rustc_hir::limit::Limit;
use rustc_span::{Symbol, sym};
use rustc_span::Symbol;
use crate::context::{AcceptContext, Stage};
use crate::parser::{ArgParser, NameValueParser};
@@ -32,36 +32,6 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
|| 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.
///
/// Used by attributes that take a single integer as argument, such as
@@ -70,7 +40,7 @@ pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(
/// `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);
+26 -9
View File
@@ -11,6 +11,7 @@
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;
@@ -19,8 +20,8 @@
};
use crate::attributes::body::CoroutineParser;
use crate::attributes::codegen_attrs::{
ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser,
NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser,
ColdParser, CoverageParser, EiiExternItemParser, ExportNameParser, ForceTargetFeatureParser,
NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser,
RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, TargetFeatureParser,
TrackCallerParser, UsedParser,
};
@@ -32,6 +33,7 @@
};
use crate::attributes::debugger::DebuggerViualizerParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::doc::DocParser;
use crate::attributes::dummy::DummyParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
use crate::attributes::link_attrs::{
@@ -73,7 +75,7 @@
};
use crate::attributes::transparency::TransparencyParser;
use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs};
use crate::parser::{ArgParser, PathParser};
use crate::parser::{ArgParser, RefPathParser};
use crate::session_diagnostics::{
AttributeParseError, AttributeParseErrorReason, ParsedDescription, UnknownMetaItem,
};
@@ -93,7 +95,7 @@ pub(super) struct GroupTypeInnerAccept<S: Stage> {
}
type AcceptFn<S> =
Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser<'a>) + Send + Sync>;
Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser) + Send + Sync>;
type FinalizeFn<S> =
Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind>>;
@@ -161,6 +163,7 @@ mod late {
BodyStabilityParser,
ConfusablesParser,
ConstStabilityParser,
DocParser,
MacroUseParser,
NakedParser,
StabilityParser,
@@ -224,6 +227,7 @@ mod late {
Single<WithoutArgs<CoroutineParser>>,
Single<WithoutArgs<DenyExplicitImplParser>>,
Single<WithoutArgs<DoNotImplementViaObjectParser>>,
Single<WithoutArgs<EiiExternItemParser>>,
Single<WithoutArgs<ExportStableParser>>,
Single<WithoutArgs<FfiConstParser>>,
Single<WithoutArgs<FfiPureParser>>,
@@ -381,7 +385,7 @@ pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuarant
/// 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: AttributeLintKind, span: Span) {
pub(crate) fn emit_lint(&mut self, lint: &'static Lint, kind: AttributeLintKind, span: Span) {
if !matches!(
self.stage.should_emit(),
ShouldEmit::ErrorsAndLints | ShouldEmit::EarlyFatal { also_emit_lints: true }
@@ -389,11 +393,12 @@ pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
return;
}
let id = self.target_id;
(self.emit_lint)(AttributeLint { id, span, kind: lint });
(self.emit_lint)(AttributeLint { lint_id: LintId::of(lint), id, span, kind });
}
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,
@@ -409,6 +414,7 @@ pub(crate) fn warn_unused_duplicate_future_error(
unused_span: Span,
) {
self.emit_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
AttributeLintKind::UnusedDuplicate {
this: unused_span,
other: used_span,
@@ -424,7 +430,7 @@ pub(crate) fn unknown_key(
&self,
span: Span,
found: String,
options: &'static [&'static str],
options: &[&'static str],
) -> ErrorGuaranteed {
self.emit_err(UnknownMetaItem { span, item: found, expected: options })
}
@@ -632,14 +638,25 @@ pub(crate) fn expected_specific_argument_strings(
}
pub(crate) fn warn_empty_attribute(&mut self, span: Span) {
let attr_path = self.attr_path.clone();
let attr_path = self.attr_path.clone().to_string();
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
@@ -697,7 +714,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 [PathParser<'p>],
pub(crate) all_attrs: &'p [RefPathParser<'p>],
}
impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> {
+90 -36
View File
@@ -1,17 +1,19 @@
use std::borrow::Cow;
use std::convert::identity;
use rustc_ast as ast;
use rustc_ast::{AttrStyle, NodeId};
use rustc_ast::token::DocFragmentKind;
use rustc_ast::{AttrStyle, NodeId, Safety};
use rustc_errors::DiagCtxtHandle;
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::AttributeLint;
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target};
use rustc_session::Session;
use rustc_session::lint::BuiltinLintDiag;
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage};
use crate::parser::{ArgParser, MetaItemParser, PathParser};
use crate::parser::{ArgParser, PathParser, RefPathParser};
use crate::session_diagnostics::ParsedDescription;
use crate::{Early, Late, OmitDoc, ShouldEmit};
@@ -115,7 +117,12 @@ pub fn parse_limited_all(
OmitDoc::Skip,
std::convert::identity,
|lint| {
crate::lints::emit_attribute_lint(&lint, sess);
sess.psess.buffer_lint(
lint.lint_id.lint,
lint.span,
lint.id,
BuiltinLintDiag::AttributeLint(lint.kind),
)
},
)
}
@@ -129,7 +136,7 @@ pub fn parse_single<T>(
target_node_id: NodeId,
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 {
@@ -137,21 +144,23 @@ pub fn parse_single<T>(
};
let parts =
normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
let meta_parser = MetaItemParser::from_attr(normal_attr, &parts, &sess.psess, emit_errors)?;
let path = meta_parser.path();
let args = meta_parser.args();
let path = AttrPath::from_ast(&normal_attr.item.path, identity);
let args =
ArgParser::from_attr_args(&normal_attr.item.args, &parts, &sess.psess, emit_errors)?;
Self::parse_single_args(
sess,
attr.span,
normal_attr.item.span(),
attr.style,
path.get_attribute_path(),
path,
Some(normal_attr.item.unsafety),
ParsedDescription::Attribute,
target_span,
target_node_id,
features,
emit_errors,
args,
&args,
parse_fn,
template,
)
@@ -165,6 +174,7 @@ pub fn parse_single_args<T, I>(
inner_span: Span,
attr_style: AttrStyle,
attr_path: AttrPath,
attr_safety: Option<Safety>,
parsed_description: ParsedDescription,
target_span: Span,
target_node_id: NodeId,
@@ -181,14 +191,29 @@ pub fn parse_single_args<T, I>(
sess,
stage: Early { emit_errors },
};
let mut emit_lint = |lint: AttributeLint<NodeId>| {
sess.psess.buffer_lint(
lint.lint_id.lint,
lint.span,
lint.id,
BuiltinLintDiag::AttributeLint(lint.kind),
)
};
if let Some(safety) = attr_safety {
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_id: target_node_id,
emit_lint: &mut |lint| {
crate::lints::emit_attribute_lint(&lint, sess);
},
emit_lint: &mut emit_lint,
},
attr_span,
inner_span,
@@ -243,7 +268,7 @@ pub fn parse_attribute_list(
mut emit_lint: impl FnMut(AttributeLint<S::Id>),
) -> Vec<Attribute> {
let mut attributes = Vec::new();
let mut attr_paths = Vec::new();
let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
for attr in attrs {
// If we're only looking for a single attribute, skip all the ones we don't care about.
@@ -258,7 +283,8 @@ pub fn parse_attribute_list(
// that's expanded right? But no, sometimes, when parsing attributes on macros,
// we already use the lowering logic and these are still there. So, when `omit_doc`
// is set we *also* want to ignore these.
if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) {
let is_doc_attribute = attr.has_name(sym::doc);
if omit_doc == OmitDoc::Skip && is_doc_attribute {
continue;
}
@@ -270,39 +296,67 @@ pub fn parse_attribute_list(
attributes.push(Attribute::Parsed(AttributeKind::DocComment {
style: attr.style,
kind: *comment_kind,
kind: DocFragmentKind::Sugared(*comment_kind),
span: lower_span(attr.span),
comment: *symbol,
}))
}
// // FIXME: make doc attributes go through a proper attribute parser
// ast::AttrKind::Normal(n) if n.has_name(sym::doc) => {
// let p = GenericMetaItemParser::from_attr(&n, self.dcx());
//
// attributes.push(Attribute::Parsed(AttributeKind::DocComment {
// style: attr.style,
// kind: CommentKind::Line,
// span: attr.span,
// comment: p.args().name_value(),
// }))
// }
ast::AttrKind::Normal(n) => {
attr_paths.push(PathParser(Cow::Borrowed(&n.item.path)));
attr_paths.push(PathParser(&n.item.path));
let attr_path = AttrPath::from_ast(&n.item.path, lower_span);
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(accepts) = S::parsers().accepters.get(parts.as_slice()) {
let Some(parser) = MetaItemParser::from_attr(
n,
let Some(args) = ArgParser::from_attr_args(
&n.item.args,
&parts,
&self.sess.psess,
self.stage.should_emit(),
) else {
continue;
};
let path = parser.path();
let args = parser.args();
// 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;
}
for accept in accepts {
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
shared: SharedContext {
@@ -312,14 +366,14 @@ pub fn parse_attribute_list(
emit_lint: &mut emit_lint,
},
attr_span: lower_span(attr.span),
inner_span: lower_span(attr.get_normal_item().span()),
inner_span: lower_span(n.item.span()),
attr_style: attr.style,
parsed_description: ParsedDescription::Attribute,
template: &accept.template,
attr_path: path.get_attribute_path(),
attr_path: attr_path.clone(),
};
(accept.accept_fn)(&mut cx, args);
(accept.accept_fn)(&mut cx, &args);
if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
Self::check_target(&accept.allowed_targets, target, &mut cx);
}
@@ -341,7 +395,7 @@ pub fn parse_attribute_list(
// );
attributes.push(Attribute::Unparsed(Box::new(AttrItem {
path: AttrPath::from_ast(&n.item.path),
path: attr_path.clone(),
args: self.lower_attr_args(&n.item.args, lower_span),
id: HashIgnoredAttrId { attr_id: attr.id },
style: attr.style,
+4 -4
View File
@@ -78,6 +78,8 @@
// tidy-alphabetical-start
#![feature(decl_macro)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![recursion_limit = "256"]
// tidy-alphabetical-end
@@ -97,7 +99,7 @@
/// like lists or name-value pairs.
pub mod parser;
mod lints;
mod safety;
mod session_diagnostics;
mod target_checking;
pub mod validate_attr;
@@ -105,12 +107,10 @@
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, is_doc_alias_attrs_contain_symbol, parse_version};
pub use attributes::util::{is_builtin_attr, 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" }
-102
View File
@@ -1,102 +0,0 @@
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,
},
)
}
}
}
+88 -79
View File
@@ -3,17 +3,17 @@
//!
//! FIXME(jdonszelmann): delete `rustc_ast/attr/mod.rs`
use std::borrow::Cow;
use std::borrow::Borrow;
use std::fmt::{Debug, Display};
use rustc_ast::token::{self, Delimiter, MetaVarKind};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AttrArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
use rustc_ast::{AttrArgs, Expr, ExprKind, LitKind, MetaItemLit, Path, StmtKind, UnOp};
use rustc_ast_pretty::pprust;
use rustc_errors::{Diag, PResult};
use rustc_hir::{self as hir, AttrPath};
use rustc_parse::exp;
use rustc_parse::parser::{Parser, PathStyle, token_descr};
use rustc_parse::parser::{ForceCollect, Parser, PathStyle, token_descr};
use rustc_session::errors::{create_lit_error, report_lit_error};
use rustc_session::parse::ParseSess;
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, sym};
@@ -26,9 +26,12 @@
};
#[derive(Clone, Debug)]
pub struct PathParser<'a>(pub Cow<'a, Path>);
pub struct PathParser<P: Borrow<Path>>(pub P);
impl<'a> PathParser<'a> {
pub type OwnedPathParser = PathParser<Path>;
pub type RefPathParser<'p> = PathParser<&'p Path>;
impl<P: Borrow<Path>> PathParser<P> {
pub fn get_attribute_path(&self) -> hir::AttrPath {
AttrPath {
segments: self.segments().copied().collect::<Vec<_>>().into_boxed_slice(),
@@ -36,16 +39,16 @@ pub fn get_attribute_path(&self) -> hir::AttrPath {
}
}
pub fn segments(&'a self) -> impl Iterator<Item = &'a Ident> {
self.0.segments.iter().map(|seg| &seg.ident)
pub fn segments(&self) -> impl Iterator<Item = &Ident> {
self.0.borrow().segments.iter().map(|seg| &seg.ident)
}
pub fn span(&self) -> Span {
self.0.span
self.0.borrow().span
}
pub fn len(&self) -> usize {
self.0.segments.len()
self.0.borrow().segments.len()
}
pub fn segments_is(&self, segments: &[Symbol]) -> bool {
@@ -76,21 +79,21 @@ pub fn starts_with(&self, segments: &[Symbol]) -> bool {
}
}
impl Display for PathParser<'_> {
impl<P: Borrow<Path>> Display for PathParser<P> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", pprust::path_to_string(&self.0))
write!(f, "{}", pprust::path_to_string(self.0.borrow()))
}
}
#[derive(Clone, Debug)]
#[must_use]
pub enum ArgParser<'a> {
pub enum ArgParser {
NoArgs,
List(MetaItemListParser<'a>),
List(MetaItemListParser),
NameValue(NameValueParser),
}
impl<'a> ArgParser<'a> {
impl ArgParser {
pub fn span(&self) -> Option<Span> {
match self {
Self::NoArgs => None,
@@ -100,7 +103,7 @@ pub fn span(&self) -> Option<Span> {
}
pub fn from_attr_args<'sess>(
value: &'a AttrArgs,
value: &AttrArgs,
parts: &[Symbol],
psess: &'sess ParseSess,
should_emit: ShouldEmit,
@@ -144,7 +147,7 @@ pub fn from_attr_args<'sess>(
///
/// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list
/// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list
pub fn list(&self) -> Option<&MetaItemListParser<'a>> {
pub fn list(&self) -> Option<&MetaItemListParser> {
match self {
Self::List(l) => Some(l),
Self::NameValue(_) | Self::NoArgs => None,
@@ -184,17 +187,17 @@ pub fn no_args(&self) -> Result<(), Span> {
///
/// Choose which one you want using the provided methods.
#[derive(Debug, Clone)]
pub enum MetaItemOrLitParser<'a> {
MetaItemParser(MetaItemParser<'a>),
pub enum MetaItemOrLitParser {
MetaItemParser(MetaItemParser),
Lit(MetaItemLit),
Err(Span, ErrorGuaranteed),
}
impl<'sess> MetaItemOrLitParser<'sess> {
pub fn parse_single(
impl MetaItemOrLitParser {
pub fn parse_single<'sess>(
parser: &mut Parser<'sess>,
should_emit: ShouldEmit,
) -> PResult<'sess, MetaItemOrLitParser<'static>> {
) -> PResult<'sess, MetaItemOrLitParser> {
let mut this = MetaItemListParserContext { parser, should_emit };
this.parse_meta_item_inner()
}
@@ -216,7 +219,7 @@ pub fn lit(&self) -> Option<&MetaItemLit> {
}
}
pub fn meta_item(&self) -> Option<&MetaItemParser<'sess>> {
pub fn meta_item(&self) -> Option<&MetaItemParser> {
match self {
MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
_ => None,
@@ -238,12 +241,12 @@ pub fn meta_item(&self) -> Option<&MetaItemParser<'sess>> {
///
/// The syntax of MetaItems can be found at <https://doc.rust-lang.org/reference/attributes.html>
#[derive(Clone)]
pub struct MetaItemParser<'a> {
path: PathParser<'a>,
args: ArgParser<'a>,
pub struct MetaItemParser {
path: OwnedPathParser,
args: ArgParser,
}
impl<'a> Debug for MetaItemParser<'a> {
impl Debug for MetaItemParser {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MetaItemParser")
.field("path", &self.path)
@@ -252,28 +255,12 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
}
}
impl<'a> MetaItemParser<'a> {
/// Create a new parser from a [`NormalAttr`], which is stored inside of any
/// [`ast::Attribute`](rustc_ast::Attribute)
pub fn from_attr<'sess>(
attr: &'a NormalAttr,
parts: &[Symbol],
psess: &'sess ParseSess,
should_emit: ShouldEmit,
) -> Option<Self> {
Some(Self {
path: PathParser(Cow::Borrowed(&attr.item.path)),
args: ArgParser::from_attr_args(&attr.item.args, parts, psess, should_emit)?,
})
}
}
impl<'a> MetaItemParser<'a> {
impl MetaItemParser {
pub fn span(&self) -> Span {
if let Some(other) = self.args.span() {
self.path.span().with_hi(other.hi())
self.path.borrow().span().with_hi(other.hi())
} else {
self.path.span()
self.path.borrow().span()
}
}
@@ -282,12 +269,12 @@ pub fn span(&self) -> Span {
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path
/// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path
/// - `#[inline]`: `inline` is a single segment path
pub fn path(&self) -> &PathParser<'a> {
pub fn path(&self) -> &OwnedPathParser {
&self.path
}
/// Gets just the args parser, without caring about the path.
pub fn args(&self) -> &ArgParser<'a> {
pub fn args(&self) -> &ArgParser {
&self.args
}
@@ -297,7 +284,7 @@ pub fn args(&self) -> &ArgParser<'a> {
/// - `#[inline]`: `inline` is a word
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path,
/// and not a word and should instead be parsed using [`path`](Self::path)
pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser<'a>> {
pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser> {
self.path().word_is(sym).then(|| self.args())
}
}
@@ -421,7 +408,7 @@ fn unsuffixed_meta_item_from_lit(
Ok(lit)
}
fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser<'static>> {
fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser> {
if let Some(MetaVarKind::Meta { has_meta_form }) = self.parser.token.is_metavar_seq() {
return if has_meta_form {
let attr_item = self
@@ -457,10 +444,10 @@ fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser<'static>> {
ArgParser::NoArgs
};
Ok(MetaItemParser { path: PathParser(Cow::Owned(path)), args })
Ok(MetaItemParser { path: PathParser(path), args })
}
fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser<'static>> {
fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser> {
if let Some(token_lit) = self.parser.eat_token_lit() {
// If a literal token is parsed, we commit to parsing a MetaItemLit for better errors
Ok(MetaItemOrLitParser::Lit(self.unsuffixed_meta_item_from_lit(token_lit)?))
@@ -488,33 +475,55 @@ fn expected_lit(&mut self) -> Diag<'sess> {
descr: token_descr(&self.parser.token),
quote_ident_sugg: None,
remove_neg_sugg: None,
label: None,
};
if let token::OpenInvisible(_) = self.parser.token.kind {
// Do not attempt to suggest anything when encountered as part of a macro expansion.
return self.parser.dcx().create_err(err);
}
// Suggest quoting idents, e.g. in `#[cfg(key = value)]`. We don't use `Token::ident` and
// don't `uninterpolate` the token to avoid suggesting anything butchered or questionable
// when macro metavariables are involved.
if self.parser.prev_token == token::Eq
&& let token::Ident(..) = self.parser.token.kind
{
let before = self.parser.token.span.shrink_to_lo();
while let token::Ident(..) = self.parser.token.kind {
self.parser.bump();
let snapshot = self.parser.create_snapshot_for_diagnostic();
let stmt = self.parser.parse_stmt_without_recovery(false, ForceCollect::No, false);
match stmt {
Ok(Some(stmt)) => {
// The user tried to write something like
// `#[deprecated(note = concat!("a", "b"))]`.
err.descr = stmt.kind.descr().to_string();
err.label = Some(stmt.span);
err.span = stmt.span;
if let StmtKind::Expr(expr) = &stmt.kind
&& let ExprKind::Unary(UnOp::Neg, val) = &expr.kind
&& let ExprKind::Lit(_) = val.kind
{
err.remove_neg_sugg = Some(InvalidMetaItemRemoveNegSugg {
negative_sign: expr.span.until(val.span),
});
} else if let StmtKind::Expr(expr) = &stmt.kind
&& let ExprKind::Path(None, Path { segments, .. }) = &expr.kind
&& segments.len() == 1
{
while let token::Ident(..) | token::Literal(_) | token::Dot =
self.parser.token.kind
{
// We've got a word, so we try to consume the rest of a potential sentence.
// We include `.` to correctly handle things like `A sentence here.`.
self.parser.bump();
}
err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg {
before: expr.span.shrink_to_lo(),
after: self.parser.prev_token.span.shrink_to_hi(),
});
}
}
Ok(None) => {}
Err(e) => {
e.cancel();
self.parser.restore_snapshot(snapshot);
}
err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg {
before,
after: self.parser.prev_token.span.shrink_to_hi(),
});
}
if self.parser.token == token::Minus
&& self
.parser
.look_ahead(1, |t| matches!(t.kind, rustc_ast::token::TokenKind::Literal { .. }))
{
err.remove_neg_sugg =
Some(InvalidMetaItemRemoveNegSugg { negative_sign: self.parser.token.span });
self.parser.bump();
self.parser.bump();
}
self.parser.dcx().create_err(err)
@@ -525,7 +534,7 @@ fn parse(
psess: &'sess ParseSess,
span: Span,
should_emit: ShouldEmit,
) -> PResult<'sess, MetaItemListParser<'static>> {
) -> PResult<'sess, MetaItemListParser> {
let mut parser = Parser::new(psess, tokens, None);
let mut this = MetaItemListParserContext { parser: &mut parser, should_emit };
@@ -548,14 +557,14 @@ fn parse(
}
#[derive(Debug, Clone)]
pub struct MetaItemListParser<'a> {
sub_parsers: ThinVec<MetaItemOrLitParser<'a>>,
pub struct MetaItemListParser {
sub_parsers: ThinVec<MetaItemOrLitParser>,
pub span: Span,
}
impl<'a> MetaItemListParser<'a> {
impl MetaItemListParser {
pub(crate) fn new<'sess>(
tokens: &'a TokenStream,
tokens: &TokenStream,
span: Span,
psess: &'sess ParseSess,
should_emit: ShouldEmit,
@@ -564,7 +573,7 @@ pub(crate) fn new<'sess>(
}
/// Lets you pick and choose as what you want to parse each element in the list
pub fn mixed(&self) -> impl Iterator<Item = &MetaItemOrLitParser<'a>> {
pub fn mixed(&self) -> impl Iterator<Item = &MetaItemOrLitParser> {
self.sub_parsers.iter()
}
@@ -579,7 +588,7 @@ pub fn is_empty(&self) -> bool {
/// Returns Some if the list contains only a single element.
///
/// Inside the Some is the parser to parse this single element.
pub fn single(&self) -> Option<&MetaItemOrLitParser<'a>> {
pub fn single(&self) -> Option<&MetaItemOrLitParser> {
let mut iter = self.mixed();
iter.next().filter(|_| iter.next().is_none())
}
+132
View File
@@ -0,0 +1,132 @@
use rustc_ast::Safety;
use rustc_feature::{AttributeSafety, BUILTIN_ATTRIBUTE_MAP};
use rustc_hir::AttrPath;
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
use rustc_session::lint::LintId;
use rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE;
use rustc_span::{Span, sym};
use crate::context::Stage;
use crate::{AttributeParser, ShouldEmit};
impl<'sess, S: Stage> AttributeParser<'sess, S> {
pub fn check_attribute_safety(
&mut self,
attr_path: &AttrPath,
attr_span: Span,
attr_safety: Safety,
emit_lint: &mut impl FnMut(AttributeLint<S::Id>),
target_id: S::Id,
) {
if matches!(self.stage.should_emit(), ShouldEmit::Nothing) {
return;
}
let name = (attr_path.segments.len() == 1).then_some(attr_path.segments[0].name);
if let Some(name) = name
&& [sym::cfg_trace, sym::cfg_attr_trace].contains(&name)
{
return;
}
// FIXME: We should retrieve this information from the attribute parsers instead of from `BUILTIN_ATTRIBUTE_MAP`
let builtin_attr_info = name.and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name));
let builtin_attr_safety = builtin_attr_info.map(|x| x.safety);
match (builtin_attr_safety, attr_safety) {
// - Unsafe builtin attribute
// - User wrote `#[unsafe(..)]`, which is permitted on any edition
(Some(AttributeSafety::Unsafe { .. }), Safety::Unsafe(..)) => {
// OK
}
// - Unsafe builtin attribute
// - User did not write `#[unsafe(..)]`
(Some(AttributeSafety::Unsafe { unsafe_since }), Safety::Default) => {
let path_span = attr_path.span;
// If the `attr_item`'s span is not from a macro, then just suggest
// wrapping it in `unsafe(...)`. Otherwise, we suggest putting the
// `unsafe(`, `)` right after and right before the opening and closing
// square bracket respectively.
let diag_span = attr_span;
// Attributes can be safe in earlier editions, and become unsafe in later ones.
//
// Use the span of the attribute's name to determine the edition: the span of the
// attribute as a whole may be inaccurate if it was emitted by a macro.
//
// See https://github.com/rust-lang/rust/issues/142182.
let emit_error = match unsafe_since {
None => true,
Some(unsafe_since) => path_span.edition() >= unsafe_since,
};
let mut not_from_proc_macro = true;
if diag_span.from_expansion()
&& let Ok(mut snippet) = self.sess.source_map().span_to_snippet(diag_span)
{
snippet.retain(|c| !c.is_whitespace());
if snippet.contains("!(") || snippet.starts_with("#[") && snippet.ends_with("]")
{
not_from_proc_macro = false;
}
}
if emit_error {
self.stage.emit_err(
self.sess,
crate::session_diagnostics::UnsafeAttrOutsideUnsafe {
span: path_span,
suggestion: not_from_proc_macro.then(|| {
crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion {
left: diag_span.shrink_to_lo(),
right: diag_span.shrink_to_hi(),
}
}),
},
);
} else {
emit_lint(AttributeLint {
lint_id: LintId::of(UNSAFE_ATTR_OUTSIDE_UNSAFE),
id: target_id,
span: path_span,
kind: AttributeLintKind::UnsafeAttrOutsideUnsafe {
attribute_name_span: path_span,
sugg_spans: not_from_proc_macro
.then(|| (diag_span.shrink_to_lo(), diag_span.shrink_to_hi())),
},
})
}
}
// - Normal builtin attribute
// - Writing `#[unsafe(..)]` is not permitted on normal builtin attributes
(None | Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => {
self.stage.emit_err(
self.sess,
crate::session_diagnostics::InvalidAttrUnsafe {
span: unsafe_span,
name: attr_path.clone(),
},
);
}
// - Normal builtin attribute
// - No explicit `#[unsafe(..)]` written.
(None | Some(AttributeSafety::Normal), Safety::Default) => {
// OK
}
(
Some(AttributeSafety::Unsafe { .. } | AttributeSafety::Normal) | None,
Safety::Safe(..),
) => {
self.sess.dcx().span_delayed_bug(
attr_span,
"`check_attribute_safety` does not expect `Safety::Safe` on attributes",
);
}
}
}
}
@@ -1,30 +1,17 @@
use std::num::IntErrorKind;
use rustc_ast::{self as ast, Path};
use rustc_ast::{self as ast};
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
};
use rustc_feature::AttributeTemplate;
use rustc_hir::{AttrPath, Target};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_hir::AttrPath;
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol};
use crate::fluent_generated as fluent;
pub(crate) enum UnsupportedLiteralReason {
Generic,
CfgString,
CfgBoolean,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_expected_one_cfg_pattern, code = E0536)]
pub(crate) struct ExpectedOneCfgPattern {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_invalid_predicate, code = E0537)]
pub(crate) struct InvalidPredicate {
@@ -34,6 +21,49 @@ pub(crate) struct InvalidPredicate {
pub predicate: String,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_doc_alias_empty)]
pub(crate) struct DocAliasEmpty<'a> {
#[primary_span]
pub span: Span,
pub attr_str: &'a str,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_doc_alias_bad_char)]
pub(crate) struct DocAliasBadChar<'a> {
#[primary_span]
pub span: Span,
pub attr_str: &'a str,
pub char_: char,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_doc_alias_start_end)]
pub(crate) struct DocAliasStartEnd<'a> {
#[primary_span]
pub span: Span,
pub attr_str: &'a str,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_doc_keyword_not_keyword)]
#[help]
pub(crate) struct DocKeywordNotKeyword {
#[primary_span]
pub span: Span,
pub keyword: Symbol,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_doc_attribute_not_attribute)]
#[help]
pub(crate) struct DocAttributeNotAttribute {
#[primary_span]
pub span: Span,
pub attribute: Symbol,
}
/// Error code: E0541
pub(crate) struct UnknownMetaItem<'a> {
pub span: Span,
@@ -187,46 +217,6 @@ pub(crate) struct InvalidReprHintNoValue {
pub name: Symbol,
}
/// Error code: E0565
// FIXME(jdonszelmann): slowly phased out
pub(crate) struct UnsupportedLiteral {
pub span: Span,
pub reason: UnsupportedLiteralReason,
pub is_bytestr: bool,
pub start_point_span: Span,
}
impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
let mut diag = Diag::new(
dcx,
level,
match self.reason {
UnsupportedLiteralReason::Generic => {
fluent::attr_parsing_unsupported_literal_generic
}
UnsupportedLiteralReason::CfgString => {
fluent::attr_parsing_unsupported_literal_cfg_string
}
UnsupportedLiteralReason::CfgBoolean => {
fluent::attr_parsing_unsupported_literal_cfg_boolean
}
},
);
diag.span(self.span);
diag.code(E0565);
if self.is_bytestr {
diag.span_suggestion(
self.start_point_span,
fluent::attr_parsing_unsupported_literal_suggestion,
"",
Applicability::MaybeIncorrect,
);
}
diag
}
}
#[derive(Diagnostic)]
#[diag(attr_parsing_invalid_repr_align_need_arg, code = E0589)]
pub(crate) struct InvalidReprAlignNeedArg {
@@ -332,13 +322,6 @@ pub(crate) struct RustcAllowedUnstablePairing {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_cfg_predicate_identifier)]
pub(crate) struct CfgPredicateIdentifier {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_deprecated_item_suggestion)]
pub(crate) struct DeprecatedItemSuggestion {
@@ -417,25 +400,6 @@ pub(crate) struct UnusedMultiple {
pub name: Symbol,
}
#[derive(LintDiagnostic)]
#[diag(attr_parsing_unused_duplicate)]
pub(crate) struct UnusedDuplicate {
#[suggestion(code = "", applicability = "machine-applicable")]
pub this: Span,
#[note]
pub other: Span,
#[warning]
pub warning: bool,
}
// FIXME(jdonszelmann): duplicated in rustc_lints, should be moved here completely.
#[derive(LintDiagnostic)]
#[diag(attr_parsing_ill_formed_attribute_input)]
pub(crate) struct IllFormedAttributeInput {
pub num_suggestions: usize,
pub suggestions: DiagArgValue,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_ill_formed_attribute_input)]
pub(crate) struct IllFormedAttributeInputLint {
@@ -501,29 +465,6 @@ pub(crate) struct EmptyConfusables {
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(attr_parsing_empty_attribute)]
#[note]
pub(crate) struct EmptyAttributeList {
#[suggestion(code = "", applicability = "machine-applicable")]
pub attr_span: Span,
pub attr_path: AttrPath,
pub valid_without_list: bool,
}
#[derive(LintDiagnostic)]
#[diag(attr_parsing_invalid_target_lint)]
#[warning]
#[help]
pub(crate) struct InvalidTargetLint {
pub name: AttrPath,
pub target: &'static str,
pub applied: DiagArgValue,
pub only: &'static str,
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub attr_span: Span,
}
#[derive(Diagnostic)]
#[help]
#[diag(attr_parsing_invalid_target)]
@@ -768,16 +709,20 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
diag.note(format!("for more information, visit <{link}>"));
}
diag.span_suggestions(
self.attr_span,
if self.suggestions.len() == 1 {
"must be of the form".to_string()
} else {
format!("try changing it to one of the following valid forms of the {description}")
},
self.suggestions,
Applicability::HasPlaceholders,
);
if self.suggestions.len() < 4 {
diag.span_suggestions(
self.attr_span,
if self.suggestions.len() == 1 {
"must be of the form".to_string()
} else {
format!(
"try changing it to one of the following valid forms of the {description}"
)
},
self.suggestions,
Applicability::HasPlaceholders,
);
}
diag
}
@@ -790,7 +735,7 @@ pub(crate) struct InvalidAttrUnsafe {
#[primary_span]
#[label]
pub span: Span,
pub name: Path,
pub name: AttrPath,
}
#[derive(Diagnostic)]
@@ -800,7 +745,7 @@ pub(crate) struct UnsafeAttrOutsideUnsafe {
#[label]
pub span: Span,
#[subdiagnostic]
pub suggestion: UnsafeAttrOutsideUnsafeSuggestion,
pub suggestion: Option<UnsafeAttrOutsideUnsafeSuggestion>,
}
#[derive(Subdiagnostic)]
@@ -846,6 +791,8 @@ pub(crate) struct InvalidMetaItem {
pub quote_ident_sugg: Option<InvalidMetaItemQuoteIdentSugg>,
#[subdiagnostic]
pub remove_neg_sugg: Option<InvalidMetaItemRemoveNegSugg>,
#[label]
pub label: Option<Span>,
}
#[derive(Subdiagnostic)]
@@ -872,16 +819,6 @@ pub(crate) struct SuffixedLiteralInAttribute {
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(attr_parsing_invalid_style)]
pub(crate) struct InvalidAttrStyle {
pub name: AttrPath,
pub is_used_as_inner: bool,
#[note]
pub target_span: Option<Span>,
pub target: Target,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_empty_link_name, code = E0454)]
pub(crate) struct EmptyLinkName {
@@ -994,3 +931,10 @@ pub(crate) struct CfgAttrBadDelim {
#[subdiagnostic]
pub sugg: MetaBadDelimSugg,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_doc_alias_malformed)]
pub(crate) struct DocAliasMalformed {
#[primary_span]
pub span: Span,
}
@@ -5,6 +5,7 @@
use rustc_feature::Features;
use rustc_hir::lints::AttributeLintKind;
use rustc_hir::{MethodKind, Target};
use rustc_span::sym;
use crate::AttributeParser;
use crate::context::{AcceptContext, Stage};
@@ -102,13 +103,31 @@ pub(crate) fn check_target(
let allowed_targets = allowed_targets.allowed_targets();
let (applied, only) = allowed_targets_applied(allowed_targets, target, cx.features);
let name = cx.attr_path.clone();
let lint = 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
};
let attr_span = cx.attr_span;
cx.emit_lint(
lint,
AttributeLintKind::InvalidTarget {
name,
target,
name: name.to_string(),
target: target.plural_name(),
only: if only { "only " } else { "" },
applied,
attr_span,
},
attr_span,
);
@@ -145,15 +164,15 @@ pub(crate) fn check_type(
return;
}
let lint = AttributeLintKind::InvalidStyle {
name: cx.attr_path.clone(),
let kind = AttributeLintKind::InvalidStyle {
name: cx.attr_path.to_string(),
is_used_as_inner: cx.attr_style == AttrStyle::Inner,
target,
target: target.name(),
target_span: cx.target_span,
};
let attr_span = cx.attr_span;
cx.emit_lint(lint, attr_span);
cx.emit_lint(rustc_session::lint::builtin::UNUSED_ATTRIBUTES, kind, attr_span);
}
}
+20 -108
View File
@@ -1,25 +1,27 @@
//! Meta-syntax validation logic of attributes for post-expansion.
use std::convert::identity;
use std::slice;
use rustc_ast::token::Delimiter;
use rustc_ast::tokenstream::DelimSpan;
use rustc_ast::{
self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, NodeId,
Path, Safety,
self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, Safety,
};
use rustc_errors::{Applicability, DiagCtxtHandle, FatalError, PResult};
use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
use rustc_errors::{Applicability, FatalError, PResult};
use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
use rustc_hir::AttrPath;
use rustc_hir::lints::AttributeLintKind;
use rustc_parse::parse_in;
use rustc_session::errors::report_lit_error;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNSAFE_ATTR_OUTSIDE_UNSAFE};
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
use rustc_session::parse::ParseSess;
use rustc_span::{Span, Symbol, sym};
use crate::{AttributeParser, Late, session_diagnostics as errors};
pub fn check_attr(psess: &ParseSess, attr: &Attribute, id: NodeId) {
pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
if attr.is_doc_comment() || attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace)
{
return;
@@ -27,9 +29,6 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute, id: NodeId) {
let builtin_attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
let builtin_attr_safety = builtin_attr_info.map(|x| x.safety);
check_attribute_safety(psess, builtin_attr_safety, attr, id);
// Check input tokens for built-in and key-value attributes.
match builtin_attr_info {
// `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
@@ -150,101 +149,6 @@ fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaIte
}
}
pub fn check_attribute_safety(
psess: &ParseSess,
builtin_attr_safety: Option<AttributeSafety>,
attr: &Attribute,
id: NodeId,
) {
let attr_item = attr.get_normal_item();
match (builtin_attr_safety, attr_item.unsafety) {
// - Unsafe builtin attribute
// - User wrote `#[unsafe(..)]`, which is permitted on any edition
(Some(AttributeSafety::Unsafe { .. }), Safety::Unsafe(..)) => {
// OK
}
// - Unsafe builtin attribute
// - User did not write `#[unsafe(..)]`
(Some(AttributeSafety::Unsafe { unsafe_since }), Safety::Default) => {
let path_span = attr_item.path.span;
// If the `attr_item`'s span is not from a macro, then just suggest
// wrapping it in `unsafe(...)`. Otherwise, we suggest putting the
// `unsafe(`, `)` right after and right before the opening and closing
// square bracket respectively.
let diag_span = attr_item.span();
// Attributes can be safe in earlier editions, and become unsafe in later ones.
//
// Use the span of the attribute's name to determine the edition: the span of the
// attribute as a whole may be inaccurate if it was emitted by a macro.
//
// See https://github.com/rust-lang/rust/issues/142182.
let emit_error = match unsafe_since {
None => true,
Some(unsafe_since) => path_span.edition() >= unsafe_since,
};
if emit_error {
psess.dcx().emit_err(errors::UnsafeAttrOutsideUnsafe {
span: path_span,
suggestion: errors::UnsafeAttrOutsideUnsafeSuggestion {
left: diag_span.shrink_to_lo(),
right: diag_span.shrink_to_hi(),
},
});
} else {
psess.buffer_lint(
UNSAFE_ATTR_OUTSIDE_UNSAFE,
path_span,
id,
BuiltinLintDiag::UnsafeAttrOutsideUnsafe {
attribute_name_span: path_span,
sugg_spans: (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()),
},
);
}
}
// - Normal builtin attribute
// - Writing `#[unsafe(..)]` is not permitted on normal builtin attributes
(None | Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => {
psess.dcx().emit_err(errors::InvalidAttrUnsafe {
span: unsafe_span,
name: attr_item.path.clone(),
});
}
// - Normal builtin attribute
// - No explicit `#[unsafe(..)]` written.
(None | Some(AttributeSafety::Normal), Safety::Default) => {
// OK
}
(
Some(AttributeSafety::Unsafe { .. } | AttributeSafety::Normal) | None,
Safety::Safe(..),
) => {
psess.dcx().span_delayed_bug(
attr_item.span(),
"`check_attribute_safety` does not expect `Safety::Safe` on attributes",
);
}
}
}
// Called by `check_builtin_meta_item` and code that manually denies
// `unsafe(...)` in `cfg`
pub fn deny_builtin_meta_unsafety(diag: DiagCtxtHandle<'_>, unsafety: Safety, name: &Path) {
// This only supports denying unsafety right now - making builtin attributes
// support unsafety will requite us to thread the actual `Attribute` through
// for the nice diagnostics.
if let Safety::Unsafe(unsafe_span) = unsafety {
diag.emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, name: name.clone() });
}
}
pub fn check_builtin_meta_item(
psess: &ParseSess,
meta: &MetaItem,
@@ -258,8 +162,11 @@ pub fn check_builtin_meta_item(
emit_malformed_attribute(psess, style, meta.span, name, template);
}
if deny_unsafety {
deny_builtin_meta_unsafety(psess.dcx(), meta.unsafety, &meta.path);
if deny_unsafety && let Safety::Unsafe(unsafe_span) = meta.unsafety {
psess.dcx().emit_err(errors::InvalidAttrUnsafe {
span: unsafe_span,
name: AttrPath::from_ast(&meta.path, identity),
});
}
}
@@ -291,15 +198,20 @@ fn emit_malformed_attribute(
suggestions.push(format!("#{inner}[{name} = \"{descr}\"]"));
}
}
// If there are too many suggestions, better remove all of them as it's just noise at this
// point.
if suggestions.len() > 3 {
suggestions.clear();
}
if should_warn(name) {
psess.buffer_lint(
ILL_FORMED_ATTRIBUTE_INPUT,
span,
ast::CRATE_NODE_ID,
BuiltinLintDiag::IllFormedAttributeInput {
BuiltinLintDiag::AttributeLint(AttributeLintKind::IllFormedAttributeInput {
suggestions: suggestions.clone(),
docs: template.docs,
},
}),
);
} else {
suggestions.sort();
@@ -52,8 +52,8 @@ pub(crate) fn relate(expected: Ty<'tcx>, found: Ty<'tcx>) -> UniverseInfo<'tcx>
pub(crate) fn report_erroneous_element(
&self,
mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
placeholder: ty::PlaceholderRegion,
error_element: RegionElement,
placeholder: ty::PlaceholderRegion<'tcx>,
error_element: RegionElement<'tcx>,
cause: ObligationCause<'tcx>,
) {
match *self {
@@ -152,8 +152,8 @@ fn nice_error<'infcx>(
fn report_erroneous_element(
&self,
mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
placeholder: ty::PlaceholderRegion,
error_element: RegionElement,
placeholder: ty::PlaceholderRegion<'tcx>,
error_element: RegionElement<'tcx>,
cause: ObligationCause<'tcx>,
) {
let tcx = mbcx.infcx.tcx;
@@ -169,23 +169,22 @@ fn report_erroneous_element(
let placeholder_region = ty::Region::new_placeholder(
tcx,
ty::Placeholder { universe: adjusted_universe.into(), bound: placeholder.bound },
ty::Placeholder::new(adjusted_universe.into(), placeholder.bound),
);
let error_region = if let RegionElement::PlaceholderRegion(error_placeholder) =
error_element
{
let adjusted_universe =
error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32());
adjusted_universe.map(|adjusted| {
ty::Region::new_placeholder(
tcx,
ty::Placeholder { universe: adjusted.into(), bound: error_placeholder.bound },
)
})
} else {
None
};
let error_region =
if let RegionElement::PlaceholderRegion(error_placeholder) = error_element {
let adjusted_universe =
error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32());
adjusted_universe.map(|adjusted| {
ty::Region::new_placeholder(
tcx,
ty::Placeholder::new(adjusted.into(), error_placeholder.bound),
)
})
} else {
None
};
debug!(?placeholder_region);
@@ -440,7 +439,7 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>(
placeholder_region: ty::Region<'tcx>,
error_region: Option<ty::Region<'tcx>>,
region_constraints: &RegionConstraintData<'tcx>,
mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin,
mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin<'tcx>,
mut universe_of_region: impl FnMut(RegionVid) -> UniverseIndex,
) -> Option<Diag<'a>> {
let placeholder_universe = match placeholder_region.kind() {
@@ -561,11 +561,8 @@ fn suggest_ref_for_dbg_args(
VarDebugInfoContents::Place(ref p) => p == place,
_ => false,
});
let arg_name = if let Some(var_info) = var_info {
var_info.name
} else {
return;
};
let Some(var_info) = var_info else { return };
let arg_name = var_info.name;
struct MatchArgFinder {
expr_span: Span,
match_arg_span: Option<Span>,
@@ -3940,13 +3937,30 @@ pub(crate) fn report_illegal_reassignment(
if let Some(decl) = local_decl
&& decl.can_be_made_mutable()
{
let is_for_loop = matches!(
decl.local_info(),
LocalInfo::User(BindingForm::Var(VarBindingForm {
opt_match_place: Some((_, match_span)),
..
})) if matches!(match_span.desugaring_kind(), Some(DesugaringKind::ForLoop))
);
let message = if is_for_loop
&& let Ok(binding_name) =
self.infcx.tcx.sess.source_map().span_to_snippet(decl.source_info.span)
{
format!("(mut {}) ", binding_name)
} else {
"mut ".to_string()
};
err.span_suggestion_verbose(
decl.source_info.span.shrink_to_lo(),
"consider making this binding mutable",
"mut ".to_string(),
message,
Applicability::MachineApplicable,
);
if !from_arg
&& !is_for_loop
&& matches!(
decl.local_info(),
LocalInfo::User(BindingForm::Var(VarBindingForm {
@@ -4504,7 +4518,9 @@ struct BreakFinder {
impl<'hir> Visitor<'hir> for BreakFinder {
fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
match ex.kind {
hir::ExprKind::Break(destination, _) => {
hir::ExprKind::Break(destination, _)
if !ex.span.is_desugaring(DesugaringKind::ForLoop) =>
{
self.found_breaks.push((destination, ex.span));
}
hir::ExprKind::Continue(destination) => {
@@ -410,6 +410,7 @@ fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
cx.add_sized_or_copy_bound_info(err, category, &path);
if let ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: _,
is_implicit_coercion: true,
unsize_to: Some(unsize_ty),
} = category
@@ -849,16 +850,10 @@ fn was_captured_by_trait_object(&self, borrow: &BorrowData<'tcx>) -> bool {
// will only ever have one item at any given time, but by using a vector, we can pop from
// it which simplifies the termination logic.
let mut queue = vec![location];
let mut target =
if let Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) = stmt {
if let Some(local) = place.as_local() {
local
} else {
return false;
}
} else {
return false;
};
let Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) = stmt else {
return false;
};
let Some(mut target) = place.as_local() else { return false };
debug!("was_captured_by_trait: target={:?} queue={:?}", target, queue);
while let Some(current_location) = queue.pop() {
@@ -1124,16 +1124,12 @@ pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpan
use self::UseSpans::*;
debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
let target = match self.body[location.block].statements.get(location.statement_index) {
Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) => {
if let Some(local) = place.as_local() {
local
} else {
return OtherUse(use_span);
}
}
_ => return OtherUse(use_span),
let Some(Statement { kind: StatementKind::Assign(box (place, _)), .. }) =
self.body[location.block].statements.get(location.statement_index)
else {
return OtherUse(use_span);
};
let Some(target) = place.as_local() else { return OtherUse(use_span) };
if self.body.local_kind(target) != LocalKind::Temp {
// operands are always temporaries.
@@ -142,12 +142,11 @@ pub(crate) fn report_mutability_error(
} else {
item_msg = access_place_desc;
let local_info = self.body.local_decls[local].local_info();
if let LocalInfo::StaticRef { def_id, .. } = *local_info {
let static_name = &self.infcx.tcx.item_name(def_id);
reason = format!(", as `{static_name}` is an immutable static item");
} else {
let LocalInfo::StaticRef { def_id, .. } = *local_info else {
bug!("is_ref_to_static return true, but not ref to static?");
}
};
let static_name = &self.infcx.tcx.item_name(def_id);
reason = format!(", as `{static_name}` is an immutable static item");
}
}
PlaceRef { local, projection: [proj_base @ .., ProjectionElem::Deref] } => {
@@ -109,15 +109,15 @@ pub(crate) enum RegionErrorKind<'tcx> {
/// The placeholder free region.
longer_fr: RegionVid,
/// The region element that erroneously must be outlived by `longer_fr`.
error_element: RegionElement,
error_element: RegionElement<'tcx>,
/// The placeholder region.
placeholder: ty::PlaceholderRegion,
placeholder: ty::PlaceholderRegion<'tcx>,
},
/// Any other lifetime error.
RegionError {
/// The origin of the region.
fr_origin: NllRegionVariableOrigin,
fr_origin: NllRegionVariableOrigin<'tcx>,
/// The region that should outlive `shorter_fr`.
longer_fr: RegionVid,
/// The region that should be shorter, but we can't prove it.
@@ -427,7 +427,7 @@ pub(crate) fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) {
pub(crate) fn report_region_error(
&mut self,
fr: RegionVid,
fr_origin: NllRegionVariableOrigin,
fr_origin: NllRegionVariableOrigin<'tcx>,
outlived_fr: RegionVid,
outlives_suggestion: &mut OutlivesSuggestionBuilder,
) {
@@ -541,6 +541,23 @@ pub(crate) fn report_region_error(
self.add_placeholder_from_predicate_note(&mut diag, &path);
self.add_sized_or_copy_bound_info(&mut diag, category, &path);
for constraint in &path {
if let ConstraintCategory::Cast { is_raw_ptr_dyn_type_cast: true, .. } =
constraint.category
{
diag.span_note(
constraint.span,
format!("raw pointer casts of trait objects cannot extend lifetimes"),
);
diag.note(format!(
"this was previously accepted by the compiler but was changed recently"
));
diag.help(format!(
"see <https://github.com/rust-lang/rust/issues/141402> for more information"
));
}
}
self.buffer_error(diag);
}
@@ -830,11 +847,9 @@ fn add_static_impl_trait_suggestion(
let fn_returns = self.infcx.tcx.return_type_impl_or_dyn_traits(suitable_region.scope);
let param = if let Some(param) =
let Some(param) =
find_param_with_region(self.infcx.tcx, self.mir_def_id(), f, outlived_f)
{
param
} else {
else {
return;
};
@@ -913,37 +928,27 @@ fn maybe_suggest_constrain_dyn_trait_impl(
let tcx = self.infcx.tcx;
let instance = if let ConstraintCategory::CallArgument(Some(func_ty)) = category {
let (fn_did, args) = match func_ty.kind() {
ty::FnDef(fn_did, args) => (fn_did, args),
_ => return,
};
debug!(?fn_did, ?args);
let ConstraintCategory::CallArgument(Some(func_ty)) = category else { return };
let ty::FnDef(fn_did, args) = func_ty.kind() else { return };
debug!(?fn_did, ?args);
// Only suggest this on function calls, not closures
let ty = tcx.type_of(fn_did).instantiate_identity();
debug!("ty: {:?}, ty.kind: {:?}", ty, ty.kind());
if let ty::Closure(_, _) = ty.kind() {
return;
}
if let Ok(Some(instance)) = ty::Instance::try_resolve(
tcx,
self.infcx.typing_env(self.infcx.param_env),
*fn_did,
self.infcx.resolve_vars_if_possible(args),
) {
instance
} else {
return;
}
} else {
// Only suggest this on function calls, not closures
let ty = tcx.type_of(fn_did).instantiate_identity();
debug!("ty: {:?}, ty.kind: {:?}", ty, ty.kind());
if let ty::Closure(_, _) = ty.kind() {
return;
}
let Ok(Some(instance)) = ty::Instance::try_resolve(
tcx,
self.infcx.typing_env(self.infcx.param_env),
*fn_did,
self.infcx.resolve_vars_if_possible(args),
) else {
return;
};
let param = match find_param_with_region(tcx, self.mir_def_id(), f, o) {
Some(param) => param,
None => return,
let Some(param) = find_param_with_region(tcx, self.mir_def_id(), f, o) else {
return;
};
debug!(?param);
@@ -32,7 +32,7 @@ pub(crate) struct LoweredConstraints<'tcx> {
pub(crate) type_tests: Vec<TypeTest<'tcx>>,
pub(crate) liveness_constraints: LivenessValues,
pub(crate) universe_causes: FxIndexMap<UniverseIndex, UniverseInfo<'tcx>>,
pub(crate) placeholder_indices: PlaceholderIndices,
pub(crate) placeholder_indices: PlaceholderIndices<'tcx>,
}
impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> {
+3 -2
View File
@@ -2,6 +2,7 @@
// tidy-alphabetical-start
#![allow(internal_features)]
#![deny(clippy::manual_let_else)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(file_buffered)]
@@ -661,7 +662,7 @@ pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId, root_def_id: LocalDefId
pub(crate) fn next_region_var<F>(
&self,
origin: RegionVariableOrigin,
origin: RegionVariableOrigin<'tcx>,
get_ctxt_fn: F,
) -> ty::Region<'tcx>
where
@@ -683,7 +684,7 @@ pub(crate) fn next_region_var<F>(
#[instrument(skip(self, get_ctxt_fn), level = "debug")]
pub(crate) fn next_nll_region_var<F>(
&self,
origin: NllRegionVariableOrigin,
origin: NllRegionVariableOrigin<'tcx>,
get_ctxt_fn: F,
) -> ty::Region<'tcx>
where
@@ -67,12 +67,11 @@ fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location:
match context {
PlaceContext::NonMutatingUse(_)
| PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
let path = match self.move_data.rev_lookup.find(place.as_ref()) {
LookupResult::Exact(path) | LookupResult::Parent(Some(path)) => path,
_ => {
// There's no path access to emit.
return;
}
let (LookupResult::Exact(path) | LookupResult::Parent(Some(path))) =
self.move_data.rev_lookup.find(place.as_ref())
else {
// There's no path access to emit.
return;
};
debug!("AccessFactsExtractor - emit path access ({path:?}, {location:?})");
self.facts.path_accessed_at_base.push((path, self.location_to_index(location)));
@@ -1,8 +1,11 @@
use std::collections::BTreeMap;
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::SparseBitMatrix;
use rustc_middle::mir::{Body, Location};
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
use rustc_middle::ty::relate::{
self, Relate, RelateResult, TypeRelation, relate_args_with_variances,
};
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable};
use rustc_mir_dataflow::points::PointIndex;
@@ -256,6 +259,20 @@ fn cx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn relate_ty_args(
&mut self,
a_ty: Ty<'tcx>,
_: Ty<'tcx>,
def_id: DefId,
a_args: ty::GenericArgsRef<'tcx>,
b_args: ty::GenericArgsRef<'tcx>,
_: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>,
) -> RelateResult<'tcx, Ty<'tcx>> {
let variances = self.cx().variances_of(def_id);
relate_args_with_variances(self, variances, a_args, b_args)?;
Ok(a_ty)
}
fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
&mut self,
variance: ty::Variance,
@@ -80,7 +80,7 @@ fn for_each_constraint(
let OutlivesConstraint { sup, sub, locations, category, span, .. } = constraint;
let (name, arg) = match locations {
Locations::All(span) => {
("All", tcx.sess.source_map().span_to_embeddable_string(*span))
("All", tcx.sess.source_map().span_to_diagnostic_string(*span))
}
Locations::Single(loc) => ("Single", format!("{loc:?}")),
};
@@ -110,7 +110,7 @@ pub struct RegionInferenceContext<'tcx> {
/// The final inferred values of the region variables; we compute
/// one value per SCC. To get the value for any given *region*,
/// you first find which scc it is a part of.
scc_values: RegionValues<ConstraintSccIndex>,
scc_values: RegionValues<'tcx, ConstraintSccIndex>,
/// Type constraints that we check after solving.
type_tests: Vec<TypeTest<'tcx>>,
@@ -125,7 +125,7 @@ pub(crate) struct RegionDefinition<'tcx> {
/// What kind of variable is this -- a free region? existential
/// variable? etc. (See the `NllRegionVariableOrigin` for more
/// info.)
pub(crate) origin: NllRegionVariableOrigin,
pub(crate) origin: NllRegionVariableOrigin<'tcx>,
/// Which universe is this region variable defined in? This is
/// most often `ty::UniverseIndex::ROOT`, but when we encounter
@@ -453,7 +453,7 @@ pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diag<'_, ()>) {
/// Returns `true` if the region `r` contains the point `p`.
///
/// Panics if called before `solve()` executes,
pub(crate) fn region_contains(&self, r: RegionVid, p: impl ToElementIndex) -> bool {
pub(crate) fn region_contains(&self, r: RegionVid, p: impl ToElementIndex<'tcx>) -> bool {
let scc = self.constraint_sccs.scc(r);
self.scc_values.contains(scc, p)
}
@@ -481,7 +481,7 @@ pub(crate) fn region_value_str(&self, r: RegionVid) -> String {
pub(crate) fn placeholders_contained_in(
&self,
r: RegionVid,
) -> impl Iterator<Item = ty::PlaceholderRegion> {
) -> impl Iterator<Item = ty::PlaceholderRegion<'tcx>> {
let scc = self.constraint_sccs.scc(r);
self.scc_values.placeholders_contained_in(scc)
}
@@ -1311,7 +1311,7 @@ fn try_propagate_universal_region_error(
fn check_bound_universal_region(
&self,
longer_fr: RegionVid,
placeholder: ty::PlaceholderRegion,
placeholder: ty::PlaceholderRegion<'tcx>,
errors_buffer: &mut RegionErrors<'tcx>,
) {
debug!("check_bound_universal_region(fr={:?}, placeholder={:?})", longer_fr, placeholder,);
@@ -1523,7 +1523,7 @@ pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location)
pub(crate) fn region_from_element(
&self,
longer_fr: RegionVid,
element: &RegionElement,
element: &RegionElement<'tcx>,
) -> RegionVid {
match *element {
RegionElement::Location(l) => self.find_sub_region_live_at(longer_fr, l),
@@ -1564,7 +1564,7 @@ pub(crate) fn universal_regions(&self) -> &UniversalRegions<'tcx> {
pub(crate) fn best_blame_constraint(
&self,
from_region: RegionVid,
from_region_origin: NllRegionVariableOrigin,
from_region_origin: NllRegionVariableOrigin<'tcx>,
to_region: RegionVid,
) -> (BlameConstraint<'tcx>, Vec<OutlivesConstraint<'tcx>>) {
assert!(from_region != to_region, "Trying to blame a region for itself!");
@@ -1697,6 +1697,7 @@ pub(crate) fn best_blame_constraint(
// should be as limited as possible; the note is prone to false positives and this
// constraint usually isn't best to blame.
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: _,
unsize_to: Some(unsize_ty),
is_implicit_coercion: true,
} if to_region == self.universal_regions().fr_static
@@ -27,7 +27,7 @@ pub(super) struct RegionCtxt<'a, 'tcx> {
pub(super) constraint_sccs: ConstraintSccs,
pub(super) scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
pub(super) rev_scc_graph: ReverseSccGraph,
pub(super) scc_values: RegionValues<ConstraintSccIndex>,
pub(super) scc_values: RegionValues<'tcx, ConstraintSccIndex>,
}
impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
@@ -10,8 +10,8 @@
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
use tracing::debug;
use crate::BorrowIndex;
use crate::polonius::LiveLoans;
use crate::{BorrowIndex, TyCtxt};
rustc_index::newtype_index! {
/// A single integer representing a `ty::Placeholder`.
@@ -22,7 +22,7 @@ pub(crate) struct PlaceholderIndex {}
/// An individual element in a region value -- the value of a
/// particular region variable consists of a set of these elements.
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum RegionElement {
pub(crate) enum RegionElement<'tcx> {
/// A point in the control-flow graph.
Location(Location),
@@ -32,7 +32,7 @@ pub(crate) enum RegionElement {
/// A placeholder (e.g., instantiated from a `for<'a> fn(&'a u32)`
/// type).
PlaceholderRegion(ty::PlaceholderRegion),
PlaceholderRegion(ty::PlaceholderRegion<'tcx>),
}
/// Records the CFG locations where each region is live. When we initially compute liveness, we use
@@ -196,25 +196,28 @@ pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, point: PointIndex) -
/// NLL.
#[derive(Debug, Default)]
#[derive(Clone)] // FIXME(#146079)
pub(crate) struct PlaceholderIndices {
indices: FxIndexSet<ty::PlaceholderRegion>,
pub(crate) struct PlaceholderIndices<'tcx> {
indices: FxIndexSet<ty::PlaceholderRegion<'tcx>>,
}
impl PlaceholderIndices {
impl<'tcx> PlaceholderIndices<'tcx> {
/// Returns the `PlaceholderIndex` for the inserted `PlaceholderRegion`
pub(crate) fn insert(&mut self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex {
pub(crate) fn insert(&mut self, placeholder: ty::PlaceholderRegion<'tcx>) -> PlaceholderIndex {
let (index, _) = self.indices.insert_full(placeholder);
index.into()
}
pub(crate) fn lookup_index(&self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex {
pub(crate) fn lookup_index(
&self,
placeholder: ty::PlaceholderRegion<'tcx>,
) -> PlaceholderIndex {
self.indices.get_index_of(&placeholder).unwrap().into()
}
pub(crate) fn lookup_placeholder(
&self,
placeholder: PlaceholderIndex,
) -> ty::PlaceholderRegion {
) -> ty::PlaceholderRegion<'tcx> {
self.indices[placeholder.index()]
}
@@ -241,9 +244,9 @@ pub(crate) fn len(&self) -> usize {
/// Here, the variable `'0` would contain the free region `'a`,
/// because (since it is returned) it must live for at least `'a`. But
/// it would also contain various points from within the function.
pub(crate) struct RegionValues<N: Idx> {
pub(crate) struct RegionValues<'tcx, N: Idx> {
location_map: Rc<DenseLocationMap>,
placeholder_indices: PlaceholderIndices,
placeholder_indices: PlaceholderIndices<'tcx>,
points: SparseIntervalMatrix<N, PointIndex>,
free_regions: SparseBitMatrix<N, RegionVid>,
@@ -252,14 +255,14 @@ pub(crate) struct RegionValues<N: Idx> {
placeholders: SparseBitMatrix<N, PlaceholderIndex>,
}
impl<N: Idx> RegionValues<N> {
impl<'tcx, N: Idx> RegionValues<'tcx, N> {
/// Creates a new set of "region values" that tracks causal information.
/// Each of the regions in num_region_variables will be initialized with an
/// empty set of points and no causal information.
pub(crate) fn new(
location_map: Rc<DenseLocationMap>,
num_universal_regions: usize,
placeholder_indices: PlaceholderIndices,
placeholder_indices: PlaceholderIndices<'tcx>,
) -> Self {
let num_points = location_map.num_points();
let num_placeholders = placeholder_indices.len();
@@ -274,7 +277,7 @@ pub(crate) fn new(
/// Adds the given element to the value for the given region. Returns whether
/// the element is newly added (i.e., was not already present).
pub(crate) fn add_element(&mut self, r: N, elem: impl ToElementIndex) -> bool {
pub(crate) fn add_element(&mut self, r: N, elem: impl ToElementIndex<'tcx>) -> bool {
debug!("add(r={:?}, elem={:?})", r, elem);
elem.add_to_row(self, r)
}
@@ -293,7 +296,7 @@ pub(crate) fn add_region(&mut self, r_to: N, r_from: N) -> bool {
}
/// Returns `true` if the region `r` contains the given element.
pub(crate) fn contains(&self, r: N, elem: impl ToElementIndex) -> bool {
pub(crate) fn contains(&self, r: N, elem: impl ToElementIndex<'tcx>) -> bool {
elem.contained_in_row(self, r)
}
@@ -359,7 +362,7 @@ pub(crate) fn universal_regions_outlived_by(&self, r: N) -> impl Iterator<Item =
pub(crate) fn placeholders_contained_in(
&self,
r: N,
) -> impl Iterator<Item = ty::PlaceholderRegion> {
) -> impl Iterator<Item = ty::PlaceholderRegion<'tcx>> {
self.placeholders
.row(r)
.into_iter()
@@ -368,7 +371,7 @@ pub(crate) fn placeholders_contained_in(
}
/// Returns all the elements contained in a given region's value.
pub(crate) fn elements_contained_in(&self, r: N) -> impl Iterator<Item = RegionElement> {
pub(crate) fn elements_contained_in(&self, r: N) -> impl Iterator<Item = RegionElement<'tcx>> {
let points_iter = self.locations_outlived_by(r).map(RegionElement::Location);
let free_regions_iter =
@@ -386,42 +389,50 @@ pub(crate) fn region_value_str(&self, r: N) -> String {
}
}
pub(crate) trait ToElementIndex: Debug + Copy {
fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool;
pub(crate) trait ToElementIndex<'tcx>: Debug + Copy {
fn add_to_row<N: Idx>(self, values: &mut RegionValues<'tcx, N>, row: N) -> bool;
fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool;
fn contained_in_row<N: Idx>(self, values: &RegionValues<'tcx, N>, row: N) -> bool;
}
impl ToElementIndex for Location {
fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool {
impl ToElementIndex<'_> for Location {
fn add_to_row<N: Idx>(self, values: &mut RegionValues<'_, N>, row: N) -> bool {
let index = values.location_map.point_from_location(self);
values.points.insert(row, index)
}
fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
fn contained_in_row<N: Idx>(self, values: &RegionValues<'_, N>, row: N) -> bool {
let index = values.location_map.point_from_location(self);
values.points.contains(row, index)
}
}
impl ToElementIndex for RegionVid {
fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool {
impl ToElementIndex<'_> for RegionVid {
fn add_to_row<N: Idx>(self, values: &mut RegionValues<'_, N>, row: N) -> bool {
values.free_regions.insert(row, self)
}
fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
fn contained_in_row<N: Idx>(self, values: &RegionValues<'_, N>, row: N) -> bool {
values.free_regions.contains(row, self)
}
}
impl ToElementIndex for ty::PlaceholderRegion {
fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool {
let index = values.placeholder_indices.lookup_index(self);
impl<'tcx> ToElementIndex<'tcx> for ty::PlaceholderRegion<'tcx> {
fn add_to_row<N: Idx>(self, values: &mut RegionValues<'tcx, N>, row: N) -> bool
where
Self: Into<ty::Placeholder<TyCtxt<'tcx>, ty::BoundRegion>>,
{
let placeholder: ty::Placeholder<TyCtxt<'tcx>, ty::BoundRegion> = self.into();
let index = values.placeholder_indices.lookup_index(placeholder);
values.placeholders.insert(row, index)
}
fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
let index = values.placeholder_indices.lookup_index(self);
fn contained_in_row<N: Idx>(self, values: &RegionValues<'tcx, N>, row: N) -> bool
where
Self: Into<ty::Placeholder<TyCtxt<'tcx>, ty::BoundRegion>>,
{
let placeholder: ty::Placeholder<TyCtxt<'tcx>, ty::BoundRegion> = self.into();
let index = values.placeholder_indices.lookup_index(placeholder);
values.placeholders.contains(row, index)
}
}
@@ -441,7 +452,9 @@ pub(crate) fn pretty_print_points(
}
/// For debugging purposes, returns a pretty-printed string of the given region elements.
fn pretty_print_region_elements(elements: impl IntoIterator<Item = RegionElement>) -> String {
fn pretty_print_region_elements<'tcx>(
elements: impl IntoIterator<Item = RegionElement<'tcx>>,
) -> String {
let mut result = String::new();
result.push('{');
+125 -50
View File
@@ -258,7 +258,7 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> {
///
/// To keep everything in sync, do not insert this set
/// directly. Instead, use the `placeholder_region` helper.
pub(crate) placeholder_indices: PlaceholderIndices,
pub(crate) placeholder_indices: PlaceholderIndices<'tcx>,
/// Each time we add a placeholder to `placeholder_indices`, we
/// also create a corresponding "representative" region vid for
@@ -289,7 +289,7 @@ impl<'tcx> MirTypeckRegionConstraints<'tcx> {
pub(crate) fn placeholder_region(
&mut self,
infcx: &InferCtxt<'tcx>,
placeholder: ty::PlaceholderRegion,
placeholder: ty::PlaceholderRegion<'tcx>,
) -> ty::Region<'tcx> {
let placeholder_index = self.placeholder_indices.insert(placeholder);
match self.placeholder_index_to_region.get(placeholder_index) {
@@ -1061,7 +1061,10 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
Rvalue::Cast(cast_kind, op, ty) => {
match *cast_kind {
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, coercion_source) => {
CastKind::PointerCoercion(
PointerCoercion::ReifyFnPointer(target_safety),
coercion_source,
) => {
let is_implicit_coercion = coercion_source == CoercionSource::Implicit;
let src_ty = op.ty(self.body, tcx);
let mut src_sig = src_ty.fn_sig(tcx);
@@ -1078,6 +1081,10 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
src_sig = safe_sig;
}
if src_sig.safety().is_safe() && target_safety.is_unsafe() {
src_sig = tcx.safe_to_unsafe_sig(src_sig);
}
// HACK: This shouldn't be necessary... We can remove this when we actually
// get binders with where clauses, then elaborate implied bounds into that
// binder, and implement a higher-ranked subtyping algorithm that actually
@@ -1106,7 +1113,11 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
self.prove_predicate(
ty::ClauseKind::WellFormed(src_ty.into()),
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
);
let src_ty = self.normalize(src_ty, location);
@@ -1114,7 +1125,11 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
src_ty,
*ty,
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
) {
span_mirbug!(
self,
@@ -1135,7 +1150,11 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
self.prove_predicate(
ty::ClauseKind::WellFormed(src_ty.into()),
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
);
// The type that we see in the fcx is like
@@ -1148,7 +1167,11 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
src_ty,
*ty,
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
) {
span_mirbug!(
self,
@@ -1177,7 +1200,11 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
ty_fn_ptr_from,
*ty,
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
) {
span_mirbug!(
self,
@@ -1210,7 +1237,11 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
ty_fn_ptr_from,
*ty,
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
) {
span_mirbug!(
self,
@@ -1239,6 +1270,7 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
trait_ref,
location.to_locations(),
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: Some(unsize_to),
},
@@ -1264,7 +1296,11 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
*ty_from,
*ty_to,
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
) {
span_mirbug!(
self,
@@ -1327,7 +1363,11 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
*ty_elem,
*ty_to,
location.to_locations(),
ConstraintCategory::Cast { is_implicit_coercion, unsize_to: None },
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion,
unsize_to: None,
},
) {
span_mirbug!(
self,
@@ -1484,55 +1524,90 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
trait_ref,
location.to_locations(),
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: false,
is_implicit_coercion: true,
unsize_to: None,
},
);
} else if let ty::Dynamic(src_tty, _src_lt) =
} else if let ty::Dynamic(src_tty, src_lt) =
*self.struct_tail(src.ty, location).kind()
&& let ty::Dynamic(dst_tty, dst_lt) =
*self.struct_tail(dst.ty, location).kind()
&& src_tty.principal().is_some()
&& dst_tty.principal().is_some()
{
// This checks (lifetime part of) vtable validity for pointer casts,
// which is irrelevant when there are aren't principal traits on
// both sides (aka only auto traits).
//
// Note that other checks (such as denying `dyn Send` -> `dyn
// Debug`) are in `rustc_hir_typeck`.
match (src_tty.principal(), dst_tty.principal()) {
(Some(_), Some(_)) => {
// This checks (lifetime part of) vtable validity for pointer casts,
// which is irrelevant when there are aren't principal traits on
// both sides (aka only auto traits).
//
// Note that other checks (such as denying `dyn Send` -> `dyn
// Debug`) are in `rustc_hir_typeck`.
// Remove auto traits.
// Auto trait checks are handled in `rustc_hir_typeck` as FCW.
let src_obj = Ty::new_dynamic(
tcx,
tcx.mk_poly_existential_predicates(
&src_tty.without_auto_traits().collect::<Vec<_>>(),
// Remove auto traits.
// Auto trait checks are handled in `rustc_hir_typeck`.
let src_obj = Ty::new_dynamic(
tcx,
tcx.mk_poly_existential_predicates(
&src_tty.without_auto_traits().collect::<Vec<_>>(),
),
src_lt,
);
let dst_obj = Ty::new_dynamic(
tcx,
tcx.mk_poly_existential_predicates(
&dst_tty.without_auto_traits().collect::<Vec<_>>(),
),
dst_lt,
);
debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj);
// Trait parameters are invariant, the only part that actually has
// subtyping here is the lifetime bound of the dyn-type.
//
// For example in `dyn Trait<'a> + 'b <: dyn Trait<'c> + 'd` we would
// require that `'a == 'c` but only that `'b: 'd`.
//
// We must not allow freely casting lifetime bounds of dyn-types as it
// may allow for inaccessible VTable methods being callable: #136702
self.sub_types(
src_obj,
dst_obj,
location.to_locations(),
ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: true,
is_implicit_coercion: false,
unsize_to: None,
},
)
.unwrap();
}
(None, None) => {
// The principalless (no non-auto traits) case:
// You can only cast `dyn Send + 'long` to `dyn Send + 'short`.
self.constraints.outlives_constraints.push(
OutlivesConstraint {
sup: src_lt.as_var(),
sub: dst_lt.as_var(),
locations: location.to_locations(),
span: location.to_locations().span(self.body),
category: ConstraintCategory::Cast {
is_raw_ptr_dyn_type_cast: true,
is_implicit_coercion: false,
unsize_to: None,
},
variance_info: ty::VarianceDiagInfo::default(),
from_closure: false,
},
);
}
(None, Some(_)) => bug!(
"introducing a principal should have errored in HIR typeck"
),
// FIXME: Once we disallow casting `*const dyn Trait + 'short`
// to `*const dyn Trait + 'long`, then this can just be `src_lt`.
dst_lt,
);
let dst_obj = Ty::new_dynamic(
tcx,
tcx.mk_poly_existential_predicates(
&dst_tty.without_auto_traits().collect::<Vec<_>>(),
),
dst_lt,
);
debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj);
self.sub_types(
src_obj,
dst_obj,
location.to_locations(),
ConstraintCategory::Cast {
is_implicit_coercion: false,
unsize_to: None,
},
)
.unwrap();
(Some(_), None) => {
bug!("dropping the principal should have been an unsizing cast")
}
}
}
}
CastKind::Transmute => {
@@ -1,5 +1,6 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::relate::{
PredicateEmittingRelation, Relate, RelateResult, StructurallyRelateAliases, TypeRelation,
};
@@ -9,7 +10,8 @@
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::traits::ObligationCause;
use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::relate::combine::{super_combine_consts, super_combine_tys};
use rustc_middle::ty::relate::combine::{combine_ty_args, super_combine_consts, super_combine_tys};
use rustc_middle::ty::relate::relate_args_invariantly;
use rustc_middle::ty::{self, FnMutDelegate, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::{bug, span_bug};
use rustc_span::{Span, Symbol, sym};
@@ -182,7 +184,7 @@ fn enter_forall<T, U>(
universe
});
let placeholder = ty::PlaceholderRegion { universe, bound: br };
let placeholder = ty::PlaceholderRegion::new(universe, br);
debug!(?placeholder);
let placeholder_reg = self.next_placeholder_region(placeholder);
debug!(?placeholder_reg);
@@ -255,7 +257,10 @@ fn next_existential_region_var(&mut self, name: Option<Symbol>) -> ty::Region<'t
}
#[instrument(skip(self), level = "debug")]
fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx> {
fn next_placeholder_region(
&mut self,
placeholder: ty::PlaceholderRegion<'tcx>,
) -> ty::Region<'tcx> {
let reg =
self.type_checker.constraints.placeholder_region(self.type_checker.infcx, placeholder);
@@ -303,6 +308,35 @@ fn cx(&self) -> TyCtxt<'tcx> {
self.type_checker.infcx.tcx
}
fn relate_ty_args(
&mut self,
a_ty: Ty<'tcx>,
b_ty: Ty<'tcx>,
def_id: DefId,
a_args: ty::GenericArgsRef<'tcx>,
b_args: ty::GenericArgsRef<'tcx>,
_: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>,
) -> RelateResult<'tcx, Ty<'tcx>> {
if self.ambient_variance == ty::Invariant {
// Avoid fetching the variance if we are in an invariant context,
// slightly improves perf.
relate_args_invariantly(self, a_args, b_args)?;
Ok(a_ty)
} else {
let variances = self.cx().variances_of(def_id);
combine_ty_args(
&self.type_checker.infcx.infcx,
self,
a_ty,
b_ty,
variances,
a_args,
b_args,
|_| a_ty,
)
}
}
#[instrument(skip(self, info), level = "trace", ret)]
fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
&mut self,
@@ -328,7 +362,7 @@ fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
let infcx = self.type_checker.infcx;
let a = self.type_checker.infcx.shallow_resolve(a);
let a = infcx.shallow_resolve(a);
assert!(!b.has_non_region_infer(), "unexpected inference var {:?}", b);
if a == b {
@@ -454,8 +454,6 @@ struct UniversalRegionsBuilder<'infcx, 'tcx> {
mir_def: LocalDefId,
}
const FR: NllRegionVariableOrigin = NllRegionVariableOrigin::FreeRegion;
impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
fn build(self) -> UniversalRegions<'tcx> {
debug!("build(mir_def={:?})", self.mir_def);
@@ -466,8 +464,12 @@ fn build(self) -> UniversalRegions<'tcx> {
assert_eq!(FIRST_GLOBAL_INDEX, self.infcx.num_region_vars());
// Create the "global" region that is always free in all contexts: 'static.
let fr_static =
self.infcx.next_nll_region_var(FR, || RegionCtxt::Free(kw::Static)).as_var();
let fr_static = self
.infcx
.next_nll_region_var(NllRegionVariableOrigin::FreeRegion, || {
RegionCtxt::Free(kw::Static)
})
.as_var();
// We've now added all the global regions. The next ones we
// add will be external.
@@ -500,7 +502,9 @@ fn build(self) -> UniversalRegions<'tcx> {
debug!(?r);
let region_vid = {
let name = r.get_name_or_anon(self.infcx.tcx);
self.infcx.next_nll_region_var(FR, || RegionCtxt::LateBound(name))
self.infcx.next_nll_region_var(NllRegionVariableOrigin::FreeRegion, || {
RegionCtxt::LateBound(name)
})
};
debug!(?region_vid);
@@ -526,7 +530,9 @@ fn build(self) -> UniversalRegions<'tcx> {
let r = ty::Region::new_late_param(self.infcx.tcx, self.mir_def.to_def_id(), kind);
let region_vid = {
let name = r.get_name_or_anon(self.infcx.tcx);
self.infcx.next_nll_region_var(FR, || RegionCtxt::LateBound(name))
self.infcx.next_nll_region_var(NllRegionVariableOrigin::FreeRegion, || {
RegionCtxt::LateBound(name)
})
};
debug!(?region_vid);
@@ -553,7 +559,9 @@ fn build(self) -> UniversalRegions<'tcx> {
let reg_vid = self
.infcx
.next_nll_region_var(FR, || RegionCtxt::Free(sym::c_dash_variadic))
.next_nll_region_var(NllRegionVariableOrigin::FreeRegion, || {
RegionCtxt::Free(sym::c_dash_variadic)
})
.as_var();
let region = ty::Region::new_var(self.infcx.tcx, reg_vid);
@@ -569,8 +577,12 @@ fn build(self) -> UniversalRegions<'tcx> {
}
}
let fr_fn_body =
self.infcx.next_nll_region_var(FR, || RegionCtxt::Free(sym::fn_body)).as_var();
let fr_fn_body = self
.infcx
.next_nll_region_var(NllRegionVariableOrigin::FreeRegion, || {
RegionCtxt::Free(sym::fn_body)
})
.as_var();
let num_universals = self.infcx.num_region_vars();
@@ -613,8 +625,10 @@ fn defining_ty(&self) -> DefiningTy<'tcx> {
debug!("defining_ty (pre-replacement): {:?}", defining_ty);
let defining_ty =
self.infcx.replace_free_regions_with_nll_infer_vars(FR, defining_ty);
let defining_ty = self.infcx.replace_free_regions_with_nll_infer_vars(
NllRegionVariableOrigin::FreeRegion,
defining_ty,
);
match *defining_ty.kind() {
ty::Closure(def_id, args) => DefiningTy::Closure(def_id, args),
@@ -638,8 +652,10 @@ fn defining_ty(&self) -> DefiningTy<'tcx> {
// Do not ICE when checking default_field_values consts with lifetimes (#135649)
&& DefKind::Field != tcx.def_kind(tcx.parent(typeck_root_def_id))
{
let args =
self.infcx.replace_free_regions_with_nll_infer_vars(FR, identity_args);
let args = self.infcx.replace_free_regions_with_nll_infer_vars(
NllRegionVariableOrigin::FreeRegion,
identity_args,
);
DefiningTy::Const(self.mir_def.to_def_id(), args)
} else {
// FIXME this line creates a dependency between borrowck and typeck.
@@ -659,7 +675,10 @@ fn defining_ty(&self) -> DefiningTy<'tcx> {
InlineConstArgsParts { parent_args: identity_args, ty },
)
.args;
let args = self.infcx.replace_free_regions_with_nll_infer_vars(FR, args);
let args = self.infcx.replace_free_regions_with_nll_infer_vars(
NllRegionVariableOrigin::FreeRegion,
args,
);
DefiningTy::InlineConst(self.mir_def.to_def_id(), args)
}
}
@@ -856,7 +875,7 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
#[instrument(skip(self), level = "debug")]
fn replace_free_regions_with_nll_infer_vars<T>(
&self,
origin: NllRegionVariableOrigin,
origin: NllRegionVariableOrigin<'tcx>,
value: T,
) -> T
where
@@ -152,6 +152,17 @@ builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept
builtin_macros_duplicate_macro_attribute = duplicated attribute
builtin_macros_eii_extern_target_expected_list = `#[eii_extern_target(...)]` expects a list of one or two elements
builtin_macros_eii_extern_target_expected_macro = `#[eii_extern_target(...)]` is only valid on macros
builtin_macros_eii_extern_target_expected_unsafe = expected this argument to be "unsafe"
.note = the second argument is optional
builtin_macros_eii_only_once = `#[{$name}]` can only be specified once
.note = specified again here
builtin_macros_eii_shared_macro_expected_function = `#[{$name}]` is only valid on functions
builtin_macros_eii_shared_macro_expected_max_one_argument = `#[{$name}]` expected no arguments or a single argument: `#[{$name}(default)]`
builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time
.cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead
.cargo_typo = there is a similar Cargo environment variable: `{$suggested_var}`
@@ -90,6 +90,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span
contract: None,
body,
define_opaque: None,
eii_impls: ThinVec::new(),
}));
let attrs = thin_vec![cx.attr_word(sym::rustc_std_internal_symbol, span)];
@@ -315,7 +315,7 @@ fn manage_cond_expr(&mut self, expr: &mut Box<Expr>) {
| ExprKind::Path(_, _)
| ExprKind::Ret(_)
| ExprKind::Try(_)
| ExprKind::TryBlock(_)
| ExprKind::TryBlock(_, _)
| ExprKind::Type(_, _)
| ExprKind::Underscore
| ExprKind::While(_, _, _)
@@ -17,7 +17,7 @@ mod llvm_enzyme {
use rustc_ast::{
self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocItemKind, BindingMode,
FnRetTy, FnSig, GenericArg, GenericArgs, GenericParamKind, Generics, ItemKind,
MetaItemInner, PatKind, Path, PathSegment, TyKind, Visibility,
MetaItemInner, MgcaDisambiguation, PatKind, Path, PathSegment, TyKind, Visibility,
};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::{Ident, Span, Symbol, sym};
@@ -346,6 +346,7 @@ pub(crate) fn expand_with_mode(
contract: None,
body: Some(d_body),
define_opaque: None,
eii_impls: ThinVec::new(),
});
let mut rustc_ad_attr =
Box::new(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_autodiff)));
@@ -558,7 +559,11 @@ fn gen_turbofish_expr(
}
GenericParamKind::Const { .. } => {
let expr = ecx.expr_path(ast::Path::from_ident(p.ident));
let anon_const = AnonConst { id: ast::DUMMY_NODE_ID, value: expr };
let anon_const = AnonConst {
id: ast::DUMMY_NODE_ID,
value: expr,
mgca_disambiguation: MgcaDisambiguation::Direct,
};
Some(AngleBracketedArg::Arg(GenericArg::Const(anon_const)))
}
GenericParamKind::Lifetime { .. } => None,
@@ -813,6 +818,7 @@ fn gen_enzyme_decl(
let anon_const = rustc_ast::AnonConst {
id: ast::DUMMY_NODE_ID,
value: ecx.expr_usize(span, 1 + x.width as usize),
mgca_disambiguation: MgcaDisambiguation::Direct,
};
TyKind::Array(ty.clone(), anon_const)
};
@@ -827,6 +833,7 @@ fn gen_enzyme_decl(
let anon_const = rustc_ast::AnonConst {
id: ast::DUMMY_NODE_ID,
value: ecx.expr_usize(span, x.width as usize),
mgca_disambiguation: MgcaDisambiguation::Direct,
};
let kind = TyKind::Array(ty.clone(), anon_const);
let ty =
+2 -7
View File
@@ -26,13 +26,7 @@ pub(crate) fn expand_cfg(
ExpandResult::Ready(match parse_cfg(cx, sp, tts) {
Ok(cfg) => {
let matches_cfg = attr::eval_config_entry(
cx.sess,
&cfg,
cx.current_expansion.lint_node_id,
ShouldEmit::ErrorsAndLints,
)
.as_bool();
let matches_cfg = attr::eval_config_entry(cx.sess, &cfg).as_bool();
MacEager::expr(cx.expr_bool(sp, matches_cfg))
}
@@ -54,6 +48,7 @@ fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result<CfgEntry,
span,
AttrStyle::Inner,
AttrPath { segments: vec![Ident::from_str("cfg")].into_boxed_slice(), span },
None,
ParsedDescription::Macro,
span,
cx.current_expansion.lint_node_id,
@@ -1,7 +1,7 @@
use rustc_ast::tokenstream::TokenStream;
use rustc_attr_parsing as attr;
use rustc_attr_parsing::{
CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, ShouldEmit, parse_cfg_select,
CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, parse_cfg_select,
};
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
use rustc_span::{Ident, Span, sym};
@@ -10,21 +10,13 @@
/// Selects the first arm whose predicate evaluates to true.
fn select_arm(ecx: &ExtCtxt<'_>, branches: CfgSelectBranches) -> Option<(TokenStream, Span)> {
let mut result = None;
for (cfg, tt, arm_span) in branches.reachable {
if let EvalConfigResult::True = attr::eval_config_entry(
&ecx.sess,
&cfg,
ecx.current_expansion.lint_node_id,
ShouldEmit::ErrorsAndLints,
) {
// FIXME(#149215) Ideally we should short-circuit here, but `eval_config_entry` currently emits lints so we cannot do this yet.
result.get_or_insert((tt, arm_span));
if let EvalConfigResult::True = attr::eval_config_entry(&ecx.sess, &cfg) {
return Some((tt, arm_span));
}
}
let wildcard = branches.wildcard.map(|(_, tt, span)| (tt, span));
result.or(wildcard)
branches.wildcard.map(|(_, tt, span)| (tt, span))
}
pub(super) fn expand_cfg_select<'cx>(
@@ -1092,6 +1092,7 @@ fn create_method(
contract: None,
body: Some(body_block),
define_opaque: None,
eii_impls: ThinVec::new(),
})),
tokens: None,
})
+447
View File
@@ -0,0 +1,447 @@
use rustc_ast::token::{Delimiter, TokenKind};
use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
use rustc_ast::{
DUMMY_NODE_ID, EiiExternTarget, EiiImpl, ItemKind, Stmt, StmtKind, ast, token, tokenstream,
};
use rustc_ast_pretty::pprust::path_to_string;
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::{Ident, Span, kw, sym};
use thin_vec::{ThinVec, thin_vec};
use crate::errors::{
EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe,
EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroExpectedFunction,
};
/// ```rust
/// #[eii]
/// fn panic_handler();
///
/// // or:
///
/// #[eii(panic_handler)]
/// fn panic_handler();
///
/// // expansion:
///
/// extern "Rust" {
/// fn panic_handler();
/// }
///
/// #[rustc_builtin_macro(eii_shared_macro)]
/// #[eii_extern_target(panic_handler)]
/// macro panic_handler() {}
/// ```
pub(crate) fn eii(
ecx: &mut ExtCtxt<'_>,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable,
) -> Vec<Annotatable> {
eii_(ecx, span, meta_item, item, false)
}
pub(crate) fn unsafe_eii(
ecx: &mut ExtCtxt<'_>,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable,
) -> Vec<Annotatable> {
eii_(ecx, span, meta_item, item, true)
}
fn eii_(
ecx: &mut ExtCtxt<'_>,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable,
impl_unsafe: bool,
) -> Vec<Annotatable> {
let span = ecx.with_def_site_ctxt(span);
let (item, stmt) = if let Annotatable::Item(item) = item {
(item, false)
} else if let Annotatable::Stmt(ref stmt) = item
&& let StmtKind::Item(ref item) = stmt.kind
{
(item.clone(), true)
} else {
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
span,
name: path_to_string(&meta_item.path),
});
return vec![item];
};
let orig_item = item.clone();
let item = *item;
let ast::Item { attrs, id: _, span: item_span, vis, kind: ItemKind::Fn(mut func), tokens: _ } =
item
else {
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
span,
name: path_to_string(&meta_item.path),
});
return vec![Annotatable::Item(Box::new(item))];
};
// Detect when this is the *second* eii attribute on an item.
let mut new_attrs = ThinVec::new();
for i in attrs {
if i.has_name(sym::eii) {
ecx.dcx().emit_err(EiiOnlyOnce {
span: i.span,
first_span: span,
name: path_to_string(&meta_item.path),
});
} else {
new_attrs.push(i);
}
}
let attrs = new_attrs;
let macro_name = if meta_item.is_word() {
func.ident
} else if let Some([first]) = meta_item.meta_item_list()
&& let Some(m) = first.meta_item()
&& m.path.segments.len() == 1
{
m.path.segments[0].ident
} else {
ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
span: meta_item.span,
name: path_to_string(&meta_item.path),
});
return vec![Annotatable::Item(orig_item)];
};
let mut return_items = Vec::new();
if func.body.is_some() {
let mut default_func = func.clone();
func.body = None;
default_func.eii_impls.push(ast::EiiImpl {
node_id: DUMMY_NODE_ID,
eii_macro_path: ast::Path::from_ident(macro_name),
impl_safety: if impl_unsafe { ast::Safety::Unsafe(span) } else { ast::Safety::Default },
span,
inner_span: macro_name.span,
is_default: true, // important!
});
return_items.push(Box::new(ast::Item {
attrs: ThinVec::new(),
id: ast::DUMMY_NODE_ID,
span,
vis: ast::Visibility { span, kind: ast::VisibilityKind::Inherited, tokens: None },
kind: ast::ItemKind::Const(Box::new(ast::ConstItem {
ident: Ident { name: kw::Underscore, span },
defaultness: ast::Defaultness::Final,
generics: ast::Generics::default(),
ty: Box::new(ast::Ty {
id: DUMMY_NODE_ID,
kind: ast::TyKind::Tup(ThinVec::new()),
span,
tokens: None,
}),
rhs: Some(ast::ConstItemRhs::Body(Box::new(ast::Expr {
id: DUMMY_NODE_ID,
kind: ast::ExprKind::Block(
Box::new(ast::Block {
stmts: thin_vec![ast::Stmt {
id: DUMMY_NODE_ID,
kind: ast::StmtKind::Item(Box::new(ast::Item {
attrs: thin_vec![], // FIXME: re-add some original attrs
id: DUMMY_NODE_ID,
span: item_span,
vis: ast::Visibility {
span,
kind: ast::VisibilityKind::Inherited,
tokens: None
},
kind: ItemKind::Fn(default_func),
tokens: None,
})),
span
}],
id: DUMMY_NODE_ID,
rules: ast::BlockCheckMode::Default,
span,
tokens: None,
}),
None,
),
span,
attrs: ThinVec::new(),
tokens: None,
}))),
define_opaque: None,
})),
tokens: None,
}))
}
let decl_span = span.to(func.sig.span);
let abi = match func.sig.header.ext {
// extern "X" fn => extern "X" {}
ast::Extern::Explicit(lit, _) => Some(lit),
// extern fn => extern {}
ast::Extern::Implicit(_) => None,
// fn => extern "Rust" {}
ast::Extern::None => Some(ast::StrLit {
symbol: sym::Rust,
suffix: None,
symbol_unescaped: sym::Rust,
style: ast::StrStyle::Cooked,
span,
}),
};
// ABI has been moved to the extern {} block, so we remove it from the fn item.
func.sig.header.ext = ast::Extern::None;
// And mark safe functions explicitly as `safe fn`.
if func.sig.header.safety == ast::Safety::Default {
func.sig.header.safety = ast::Safety::Safe(func.sig.span);
}
// extern "…" { safe fn item(); }
let mut extern_item_attrs = attrs.clone();
extern_item_attrs.push(ast::Attribute {
kind: ast::AttrKind::Normal(Box::new(ast::NormalAttr {
item: ast::AttrItem {
unsafety: ast::Safety::Default,
// Add the rustc_eii_extern_item on the foreign item. Usually, foreign items are mangled.
// This attribute makes sure that we later know that this foreign item's symbol should not be.
path: ast::Path::from_ident(Ident::new(sym::rustc_eii_extern_item, span)),
args: ast::AttrArgs::Empty,
tokens: None,
},
tokens: None,
})),
id: ecx.sess.psess.attr_id_generator.mk_attr_id(),
style: ast::AttrStyle::Outer,
span,
});
let extern_block = Box::new(ast::Item {
attrs: ast::AttrVec::default(),
id: ast::DUMMY_NODE_ID,
span,
vis: ast::Visibility { span, kind: ast::VisibilityKind::Inherited, tokens: None },
kind: ast::ItemKind::ForeignMod(ast::ForeignMod {
extern_span: span,
safety: ast::Safety::Unsafe(span),
abi,
items: From::from([Box::new(ast::ForeignItem {
attrs: extern_item_attrs,
id: ast::DUMMY_NODE_ID,
span: item_span,
vis,
kind: ast::ForeignItemKind::Fn(func.clone()),
tokens: None,
})]),
}),
tokens: None,
});
let mut macro_attrs = attrs.clone();
macro_attrs.push(
// #[builtin_macro(eii_shared_macro)]
ast::Attribute {
kind: ast::AttrKind::Normal(Box::new(ast::NormalAttr {
item: ast::AttrItem {
unsafety: ast::Safety::Default,
path: ast::Path::from_ident(Ident::new(sym::rustc_builtin_macro, span)),
args: ast::AttrArgs::Delimited(ast::DelimArgs {
dspan: DelimSpan::from_single(span),
delim: Delimiter::Parenthesis,
tokens: TokenStream::new(vec![tokenstream::TokenTree::token_alone(
token::TokenKind::Ident(sym::eii_shared_macro, token::IdentIsRaw::No),
span,
)]),
}),
tokens: None,
},
tokens: None,
})),
id: ecx.sess.psess.attr_id_generator.mk_attr_id(),
style: ast::AttrStyle::Outer,
span,
},
);
let macro_def = Box::new(ast::Item {
attrs: macro_attrs,
id: ast::DUMMY_NODE_ID,
span,
// pub
vis: ast::Visibility { span, kind: ast::VisibilityKind::Public, tokens: None },
kind: ast::ItemKind::MacroDef(
// macro macro_name
macro_name,
ast::MacroDef {
// { () => {} }
body: Box::new(ast::DelimArgs {
dspan: DelimSpan::from_single(span),
delim: Delimiter::Brace,
tokens: TokenStream::from_iter([
TokenTree::Delimited(
DelimSpan::from_single(span),
DelimSpacing::new(Spacing::Alone, Spacing::Alone),
Delimiter::Parenthesis,
TokenStream::default(),
),
TokenTree::token_alone(TokenKind::FatArrow, span),
TokenTree::Delimited(
DelimSpan::from_single(span),
DelimSpacing::new(Spacing::Alone, Spacing::Alone),
Delimiter::Brace,
TokenStream::default(),
),
]),
}),
macro_rules: false,
// #[eii_extern_target(func.ident)]
eii_extern_target: Some(ast::EiiExternTarget {
extern_item_path: ast::Path::from_ident(func.ident),
impl_unsafe,
span: decl_span,
}),
},
),
tokens: None,
});
return_items.push(extern_block);
return_items.push(macro_def);
if stmt {
return_items
.into_iter()
.map(|i| {
Annotatable::Stmt(Box::new(Stmt {
id: DUMMY_NODE_ID,
kind: StmtKind::Item(i),
span,
}))
})
.collect()
} else {
return_items.into_iter().map(|i| Annotatable::Item(i)).collect()
}
}
pub(crate) fn eii_extern_target(
ecx: &mut ExtCtxt<'_>,
span: Span,
meta_item: &ast::MetaItem,
mut item: Annotatable,
) -> Vec<Annotatable> {
let i = if let Annotatable::Item(ref mut item) = item {
item
} else if let Annotatable::Stmt(ref mut stmt) = item
&& let StmtKind::Item(ref mut item) = stmt.kind
{
item
} else {
ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
return vec![item];
};
let ItemKind::MacroDef(_, d) = &mut i.kind else {
ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
return vec![item];
};
let Some(list) = meta_item.meta_item_list() else {
ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
return vec![item];
};
if list.len() > 2 {
ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
return vec![item];
}
let Some(extern_item_path) = list.get(0).and_then(|i| i.meta_item()).map(|i| i.path.clone())
else {
ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
return vec![item];
};
let impl_unsafe = if let Some(i) = list.get(1) {
if i.lit().and_then(|i| i.kind.str()).is_some_and(|i| i == kw::Unsafe) {
true
} else {
ecx.dcx().emit_err(EiiExternTargetExpectedUnsafe { span: i.span() });
return vec![item];
}
} else {
false
};
d.eii_extern_target = Some(EiiExternTarget { extern_item_path, impl_unsafe, span });
// Return the original item and the new methods.
vec![item]
}
/// all Eiis share this function as the implementation for their attribute.
pub(crate) fn eii_shared_macro(
ecx: &mut ExtCtxt<'_>,
span: Span,
meta_item: &ast::MetaItem,
mut item: Annotatable,
) -> Vec<Annotatable> {
let i = if let Annotatable::Item(ref mut item) = item {
item
} else if let Annotatable::Stmt(ref mut stmt) = item
&& let StmtKind::Item(ref mut item) = stmt.kind
{
item
} else {
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
span,
name: path_to_string(&meta_item.path),
});
return vec![item];
};
let ItemKind::Fn(f) = &mut i.kind else {
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
span,
name: path_to_string(&meta_item.path),
});
return vec![item];
};
let is_default = if meta_item.is_word() {
false
} else if let Some([first]) = meta_item.meta_item_list()
&& let Some(m) = first.meta_item()
&& m.path.segments.len() == 1
{
m.path.segments[0].ident.name == kw::Default
} else {
ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
span: meta_item.span,
name: path_to_string(&meta_item.path),
});
return vec![item];
};
f.eii_impls.push(EiiImpl {
node_id: DUMMY_NODE_ID,
eii_macro_path: meta_item.path.clone(),
impl_safety: meta_item.unsafety,
span,
inner_span: meta_item.path.span,
is_default,
});
vec![item]
}
@@ -1010,3 +1010,51 @@ pub(crate) struct CfgSelectUnreachable {
#[label]
pub wildcard_span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_eii_extern_target_expected_macro)]
pub(crate) struct EiiExternTargetExpectedMacro {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_eii_extern_target_expected_list)]
pub(crate) struct EiiExternTargetExpectedList {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_eii_extern_target_expected_unsafe)]
pub(crate) struct EiiExternTargetExpectedUnsafe {
#[primary_span]
#[note]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_eii_shared_macro_expected_function)]
pub(crate) struct EiiSharedMacroExpectedFunction {
#[primary_span]
pub span: Span,
pub name: String,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_eii_only_once)]
pub(crate) struct EiiOnlyOnce {
#[primary_span]
pub span: Span,
#[note]
pub first_span: Span,
pub name: String,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_eii_shared_macro_expected_max_one_argument)]
pub(crate) struct EiiMacroExpectedMaxOneArgument {
#[primary_span]
pub span: Span,
pub name: String,
}
@@ -84,6 +84,7 @@ fn allocator_fn(&self, method: &AllocatorMethod) -> Stmt {
contract: None,
body,
define_opaque: None,
eii_impls: ThinVec::new(),
}));
let item = self.cx.item(self.span, self.attrs(method), kind);
self.cx.stmt_item(self.ty_span, item)
+5
View File
@@ -38,6 +38,7 @@
mod derive;
mod deriving;
mod edition_panic;
mod eii;
mod env;
mod errors;
mod format;
@@ -117,9 +118,13 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
define_opaque: define_opaque::expand,
derive: derive::Expander { is_const: false },
derive_const: derive::Expander { is_const: true },
eii: eii::eii,
eii_extern_target: eii::eii_extern_target,
eii_shared_macro: eii::eii_shared_macro,
global_allocator: global_allocator::expand,
test: test::expand_test,
test_case: test::expand_test_case,
unsafe_eii: eii::unsafe_eii,
// tidy-alphabetical-end
}
@@ -1,5 +1,5 @@
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AnonConst, DUMMY_NODE_ID, Ty, TyPat, TyPatKind, ast, token};
use rustc_ast::{AnonConst, DUMMY_NODE_ID, MgcaDisambiguation, Ty, TyPat, TyPatKind, ast, token};
use rustc_errors::PResult;
use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
use rustc_parse::exp;
@@ -60,8 +60,20 @@ fn ty_pat(kind: TyPatKind, span: Span) -> TyPat {
fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> TyPat {
let kind = match pat.kind {
ast::PatKind::Range(start, end, include_end) => TyPatKind::Range(
start.map(|value| Box::new(AnonConst { id: DUMMY_NODE_ID, value })),
end.map(|value| Box::new(AnonConst { id: DUMMY_NODE_ID, value })),
start.map(|value| {
Box::new(AnonConst {
id: DUMMY_NODE_ID,
value,
mgca_disambiguation: MgcaDisambiguation::Direct,
})
}),
end.map(|value| {
Box::new(AnonConst {
id: DUMMY_NODE_ID,
value,
mgca_disambiguation: MgcaDisambiguation::Direct,
})
}),
include_end,
),
ast::PatKind::Or(variants) => {
@@ -68,13 +68,10 @@ pub(crate) fn expand_file(
let topmost = cx.expansion_cause().unwrap_or(sp);
let loc = cx.source_map().lookup_char_pos(topmost.lo());
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
use rustc_span::RemapPathScopeComponents;
ExpandResult::Ready(MacEager::expr(cx.expr_str(
topmost,
Symbol::intern(
&loc.file.name.for_scope(cx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(),
),
Symbol::intern(&loc.file.name.display(RemapPathScopeComponents::MACRO).to_string_lossy()),
)))
}
+24 -19
View File
@@ -11,7 +11,7 @@
use rustc_expand::base::*;
use rustc_hir::Attribute;
use rustc_hir::attrs::AttributeKind;
use rustc_span::{ErrorGuaranteed, FileNameDisplayPreference, Ident, Span, Symbol, sym};
use rustc_span::{ErrorGuaranteed, Ident, RemapPathScopeComponents, Span, Symbol, sym};
use thin_vec::{ThinVec, thin_vec};
use tracing::debug;
@@ -34,10 +34,6 @@ pub(crate) fn expand_test_case(
check_builtin_macro_attribute(ecx, meta_item, sym::test_case);
warn_on_duplicate_attribute(ecx, &anno_item, sym::test_case);
if !ecx.ecfg.should_test {
return vec![];
}
let sp = ecx.with_def_site_ctxt(attr_sp);
let (mut item, is_stmt) = match anno_item {
Annotatable::Item(item) => (item, false),
@@ -54,6 +50,10 @@ pub(crate) fn expand_test_case(
}
};
if !ecx.ecfg.should_test {
return vec![];
}
// `#[test_case]` is valid on functions, consts, and statics. Only modify
// the item in those cases.
match &mut item.kind {
@@ -113,22 +113,17 @@ pub(crate) fn expand_test_or_bench(
item: Annotatable,
is_bench: bool,
) -> Vec<Annotatable> {
// If we're not in test configuration, remove the annotated item
if !cx.ecfg.should_test {
return vec![];
}
let (item, is_stmt) = match item {
Annotatable::Item(i) => (i, false),
Annotatable::Stmt(box ast::Stmt { kind: ast::StmtKind::Item(i), .. }) => (i, true),
other => {
not_testable_error(cx, attr_sp, None);
not_testable_error(cx, is_bench, attr_sp, None);
return vec![other];
}
};
let ast::ItemKind::Fn(fn_) = &item.kind else {
not_testable_error(cx, attr_sp, Some(&item));
not_testable_error(cx, is_bench, attr_sp, Some(&item));
return if is_stmt {
vec![Annotatable::Stmt(Box::new(cx.stmt_item(item.span, item)))]
} else {
@@ -136,6 +131,11 @@ pub(crate) fn expand_test_or_bench(
};
};
// If we're not in test configuration, remove the annotated item
if !cx.ecfg.should_test {
return vec![];
}
if let Some(attr) = attr::find_by_name(&item.attrs, sym::naked) {
cx.dcx().emit_err(errors::NakedFunctionTestingAttribute {
testing_span: attr_sp,
@@ -405,9 +405,10 @@ pub(crate) fn expand_test_or_bench(
}
}
fn not_testable_error(cx: &ExtCtxt<'_>, attr_sp: Span, item: Option<&ast::Item>) {
fn not_testable_error(cx: &ExtCtxt<'_>, is_bench: bool, attr_sp: Span, item: Option<&ast::Item>) {
let dcx = cx.dcx();
let msg = "the `#[test]` attribute may only be used on a non-associated function";
let name = if is_bench { "bench" } else { "test" };
let msg = format!("the `#[{name}]` attribute may only be used on a free function");
let level = match item.map(|i| &i.kind) {
// These were a warning before #92959 and need to continue being that to avoid breaking
// stable user code (#94508).
@@ -426,12 +427,16 @@ fn not_testable_error(cx: &ExtCtxt<'_>, attr_sp: Span, item: Option<&ast::Item>)
),
);
}
err.with_span_label(attr_sp, "the `#[test]` macro causes a function to be run as a test and has no effect on non-functions")
.with_span_suggestion(attr_sp,
err.span_label(attr_sp, format!("the `#[{name}]` macro causes a function to be run as a test and has no effect on non-functions"));
if !is_bench {
err.with_span_suggestion(attr_sp,
"replace with conditional compilation to make the item only exist when tests are being run",
"#[cfg(test)]",
Applicability::MaybeIncorrect)
.emit();
Applicability::MaybeIncorrect).emit();
} else {
err.emit();
}
}
fn get_location_info(cx: &ExtCtxt<'_>, fn_: &ast::Fn) -> (Symbol, usize, usize, usize, usize) {
@@ -440,7 +445,7 @@ fn get_location_info(cx: &ExtCtxt<'_>, fn_: &ast::Fn) -> (Symbol, usize, usize,
cx.sess.source_map().span_to_location_info(span);
let file_name = match source_file {
Some(sf) => sf.name.display(FileNameDisplayPreference::Remapped).to_string(),
Some(sf) => sf.name.display(RemapPathScopeComponents::MACRO).to_string(),
None => "no-location".to_string(),
};
@@ -345,6 +345,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box<ast::Item> {
contract: None,
body: Some(main_body),
define_opaque: None,
eii_impls: ThinVec::new(),
}));
let main = Box::new(ast::Item {
+1 -1
View File
@@ -1,7 +1,7 @@
task:
name: freebsd
freebsd_instance:
image_family: freebsd-14-2
image_family: freebsd-15-0-amd64-ufs
setup_rust_script:
- pkg install -y git-tiny binutils
- curl https://sh.rustup.rs -sSf --output rustup.sh
+87 -72
View File
@@ -10,15 +10,15 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "anyhow"
version = "1.0.98"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "arbitrary"
version = "1.4.1"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "bitflags"
@@ -37,48 +37,48 @@ dependencies = [
[[package]]
name = "cfg-if"
version = "1.0.1"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cranelift-assembler-x64"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f502c60b6af2025c312b37788c089943ef03156a2910da1aa046bb39eb8f61c7"
checksum = "bf7631e609c97f063f9777aae405e8492abf9bf92336d7aa3f875403dd4ffd7d"
dependencies = [
"cranelift-assembler-x64-meta",
]
[[package]]
name = "cranelift-assembler-x64-meta"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b7e21a74bcf08443a4ef800a4a257063e5c51ee4d7a3bd58da5262d10340830"
checksum = "9c030edccdc4a5bbf28fbfe7701b5cd1f9854b4445184dd34af2a7e8f8db6f45"
dependencies = [
"cranelift-srcgen",
]
[[package]]
name = "cranelift-bforest"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f337d268865c292ad5df0669a9bbf6223ca41460292a20ad5b0a57b8e9f27f93"
checksum = "bb544c1242d0ca98baf01873ebba96c79d5df155d5108d9bb699aefc741f5e6d"
dependencies = [
"cranelift-entity",
]
[[package]]
name = "cranelift-bitset"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0e60319a8242c8d1c7b5a2444d140c416f903f75e0d84da3256fceb822bab85"
checksum = "f0325aecbafec053d3d3f082edfdca7937e2945e7f09c5ff9672e05198312282"
[[package]]
name = "cranelift-codegen"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78dee669e447a1c68760bf7acee33835e99d564f0137b067f74d4718dfc9970d"
checksum = "abb3236fd319ae897ba00c8a25105081de5c1348576def0e96c062ad259f87a7"
dependencies = [
"bumpalo",
"cranelift-assembler-x64",
@@ -102,9 +102,9 @@ dependencies = [
[[package]]
name = "cranelift-codegen-meta"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "601f629d172b7230f41dd0e78ee797efaf7ec1a5e113c8f395f4027dff6a92ca"
checksum = "7b8791c911a361c539130ace34fb726b16aca4216470ec75d75264b1495c8a3a"
dependencies = [
"cranelift-assembler-x64-meta",
"cranelift-codegen-shared",
@@ -114,33 +114,33 @@ dependencies = [
[[package]]
name = "cranelift-codegen-shared"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15755c2660902c7d59d96f6551a66ef629650dc3fd405f9dad841e8c58c1a4a2"
checksum = "12ead718c2a10990870c19b2497b5a04b8aae6024485e33da25b5d02e35819e0"
[[package]]
name = "cranelift-control"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "727bfca18705101a294ab9077ad214a8b762ea2bc9844389d0db233d7c61ec3b"
checksum = "c0a57fc972b5651047efddccb99440d103d9d8c13393ccebde15ddd5b6a1181b"
dependencies = [
"arbitrary",
]
[[package]]
name = "cranelift-entity"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15564c6f0c72750ca4374f40b044857cbc8087571e46d4c7ccdbdcc29b1dec8b"
checksum = "5aae980b4a1678b601eab2f52e372ed0b3c9565a31c17f380008cb97b3a699c5"
dependencies = [
"cranelift-bitset",
]
[[package]]
name = "cranelift-frontend"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16c681f2731f1cf68eed9f3b6811571823a5ac498f59c52b73736b68599defb3"
checksum = "a78877016b607982ca1708c0dd4ce23bde04581a39854c9b43a1dca43625b54c"
dependencies = [
"cranelift-codegen",
"log",
@@ -150,15 +150,15 @@ dependencies = [
[[package]]
name = "cranelift-isle"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40cedc02f08307da019a3e06d3f20f772f829ff813aec975accb012f8930b688"
checksum = "5dc46a68b46d4f53f9f2f02ab8d3a34b00f03a21c124a7a965b8cbf5fdb6773b"
[[package]]
name = "cranelift-jit"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2864461448c72d15ae3311ea63df9c7e35f22f04683785f6715a0cf17e6577d"
checksum = "7df920009af919ad9df52eb7b47b1895145822e0c29da9b715a876fc8ecc6d82"
dependencies = [
"anyhow",
"cranelift-codegen",
@@ -176,9 +176,9 @@ dependencies = [
[[package]]
name = "cranelift-module"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b31d249bbbccc4c1ae54701087d4d49d05951897691eef44f4a60e70252743b"
checksum = "ddcf313629071ce74de8e59f02092f5453d1a01047607fc4ad36886b8bd1486c"
dependencies = [
"anyhow",
"cranelift-codegen",
@@ -187,9 +187,9 @@ dependencies = [
[[package]]
name = "cranelift-native"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db03ab51c60710eb83d0217725b77db4062aca83b35359f5e6aa99ed1c275977"
checksum = "03faa07ec8cf373250a8252eb773d098ff88259fa1c19ee1ecde8012839f4097"
dependencies = [
"cranelift-codegen",
"libc",
@@ -198,9 +198,9 @@ dependencies = [
[[package]]
name = "cranelift-object"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7131e0eb45ee10b0bd6082d0c0114c2e9a670b034d46774b39d0fc5c0ed7cedf"
checksum = "7cca62c14f3c2e4f438192562bbf82d1a98a59543cc66ba04fb658ba99f515a6"
dependencies = [
"anyhow",
"cranelift-codegen",
@@ -213,9 +213,9 @@ dependencies = [
[[package]]
name = "cranelift-srcgen"
version = "0.125.1"
version = "0.126.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7a06c330b7994a891ad5b622ebc9aefcd17beae832dd25f577cf60c13426bf"
checksum = "0484cb32c527a742e1bba09ef174acac0afb1dcf623ef1adda42849200edcd2e"
[[package]]
name = "crc32fast"
@@ -246,9 +246,9 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "gimli"
version = "0.32.0"
version = "0.32.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93563d740bc9ef04104f9ed6f86f1e3275c2cdafb95664e26584b9ca807a8ffe"
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
dependencies = [
"fallible-iterator",
"indexmap",
@@ -282,18 +282,18 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.174"
version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]]
name = "libloading"
version = "0.8.8"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60"
dependencies = [
"cfg-if",
"windows-targets 0.53.3",
"windows-link 0.2.1",
]
[[package]]
@@ -304,9 +304,9 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]]
name = "log"
version = "0.4.27"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "mach2"
@@ -319,9 +319,9 @@ dependencies = [
[[package]]
name = "memchr"
version = "2.7.5"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "object"
@@ -337,27 +337,27 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.95"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regalloc2"
version = "0.13.2"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efd8138ce7c3d7c13be4f61893154b5d711bd798d2d7be3ecb8dcc7e7a06ca98"
checksum = "4e249c660440317032a71ddac302f25f1d5dff387667bcc3978d1f77aa31ac34"
dependencies = [
"allocator-api2",
"bumpalo",
@@ -405,18 +405,27 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.219"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
@@ -431,15 +440,15 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "syn"
version = "2.0.104"
version = "2.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
dependencies = [
"proc-macro2",
"quote",
@@ -448,21 +457,21 @@ dependencies = [
[[package]]
name = "target-lexicon"
version = "0.13.2"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a"
checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c"
[[package]]
name = "unicode-ident"
version = "1.0.18"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "wasmtime-internal-jit-icache-coherence"
version = "38.0.1"
version = "39.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d0a76f1a6e887cc1b551b02dfd6e2ce5f6738e8cacd9ad7284f6ac1aac4698f"
checksum = "3f67986f5c499274ae5b2ba5b173bba0b95d1381f5ca70d8eec657f2392117d8"
dependencies = [
"anyhow",
"cfg-if",
@@ -472,9 +481,9 @@ dependencies = [
[[package]]
name = "wasmtime-internal-math"
version = "38.0.1"
version = "39.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b900df4252ad86547e7f2b2c00201b006db4e864893bedfb3aca32b23d81868a"
checksum = "a681733e9b5d5d8804ee6cacd59f92c0d87ba2274f42ee1d4e5a943828d0075d"
dependencies = [
"libm",
]
@@ -485,6 +494,12 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.52.0"
@@ -525,7 +540,7 @@ version = "0.53.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
dependencies = [
"windows-link",
"windows-link 0.1.3",
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0",
+13 -13
View File
@@ -8,28 +8,28 @@ crate-type = ["dylib"]
[dependencies]
# These have to be in sync with each other
cranelift-codegen = { version = "0.125.0", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] }
cranelift-frontend = { version = "0.125.0" }
cranelift-module = { version = "0.125.0" }
cranelift-native = { version = "0.125.0" }
cranelift-jit = { version = "0.125.0", optional = true }
cranelift-object = { version = "0.125.0" }
cranelift-codegen = { version = "0.126.0", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] }
cranelift-frontend = { version = "0.126.0" }
cranelift-module = { version = "0.126.0" }
cranelift-native = { version = "0.126.0" }
cranelift-jit = { version = "0.126.0", optional = true }
cranelift-object = { version = "0.126.0" }
target-lexicon = "0.13"
gimli = { version = "0.32", default-features = false, features = ["write"] }
object = { version = "0.37.3", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
indexmap = "2.0.0"
libloading = { version = "0.8.0", optional = true }
libloading = { version = "0.9.0", optional = true }
smallvec = "1.8.1"
[patch.crates-io]
# Uncomment to use an unreleased version of cranelift
#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-38.0.0" }
#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-38.0.0" }
#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-38.0.0" }
#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-38.0.0" }
#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-38.0.0" }
#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-38.0.0" }
#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-39.0.0" }
#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-39.0.0" }
#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-39.0.0" }
#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-39.0.0" }
#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-39.0.0" }
#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-39.0.0" }
# Uncomment to use local checkout of cranelift
#cranelift-codegen = { path = "../wasmtime/cranelift/codegen" }
@@ -11,7 +11,7 @@
"abi-cafe",
);
static ABI_CAFE: CargoProject = CargoProject::new(&ABI_CAFE_REPO.source_dir(), "abi_cafe_target");
static ABI_CAFE: CargoProject = CargoProject::new(ABI_CAFE_REPO.source_dir(), "abi_cafe_target");
pub(crate) fn run(
sysroot_kind: SysrootKind,
@@ -39,7 +39,26 @@ pub(crate) fn benchmark(dirs: &Dirs, compiler: &Compiler) {
let rustc_clif = &compiler.rustc;
let rustflags = &compiler.rustflags.join("\x1f");
let manifest_path = SIMPLE_RAYTRACER_REPO.source_dir().to_path(dirs).join("Cargo.toml");
let target_dir = dirs.build_dir.join("simple_raytracer");
let target_dir = dirs.build_dir.join("simple-raytracer_target");
let raytracer_cg_llvm = dirs
.build_dir
.join(get_file_name(&compiler.rustc, "raytracer_cg_llvm", "bin"))
.to_str()
.unwrap()
.to_owned();
let raytracer_cg_clif = dirs
.build_dir
.join(get_file_name(&compiler.rustc, "raytracer_cg_clif", "bin"))
.to_str()
.unwrap()
.to_owned();
let raytracer_cg_clif_opt = dirs
.build_dir
.join(get_file_name(&compiler.rustc, "raytracer_cg_clif_opt", "bin"))
.to_str()
.unwrap()
.to_owned();
let clean_cmd = format!(
"RUSTC=rustc cargo clean --manifest-path {manifest_path} --target-dir {target_dir}",
@@ -47,19 +66,19 @@ pub(crate) fn benchmark(dirs: &Dirs, compiler: &Compiler) {
target_dir = target_dir.display(),
);
let llvm_build_cmd = format!(
"RUSTC=rustc cargo build --manifest-path {manifest_path} --target-dir {target_dir} && (rm build/raytracer_cg_llvm || true) && ln build/simple_raytracer/debug/main build/raytracer_cg_llvm",
"RUSTC=rustc cargo build --manifest-path {manifest_path} --target-dir {target_dir} && (rm {raytracer_cg_llvm} || true) && ln {target_dir}/debug/main {raytracer_cg_llvm}",
manifest_path = manifest_path.display(),
target_dir = target_dir.display(),
);
let clif_build_cmd = format!(
"RUSTC={rustc_clif} CARGO_ENCODED_RUSTFLAGS=\"{rustflags}\" {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} && (rm build/raytracer_cg_clif || true) && ln build/simple_raytracer/debug/main build/raytracer_cg_clif",
"RUSTC={rustc_clif} CARGO_ENCODED_RUSTFLAGS=\"{rustflags}\" {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} && (rm {raytracer_cg_clif} || true) && ln {target_dir}/debug/main {raytracer_cg_clif}",
cargo_clif = cargo_clif.display(),
rustc_clif = rustc_clif.display(),
manifest_path = manifest_path.display(),
target_dir = target_dir.display(),
);
let clif_build_opt_cmd = format!(
"RUSTC={rustc_clif} CARGO_ENCODED_RUSTFLAGS=\"{rustflags}\" {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} --release && (rm build/raytracer_cg_clif_opt || true) && ln build/simple_raytracer/release/main build/raytracer_cg_clif_opt",
"RUSTC={rustc_clif} CARGO_ENCODED_RUSTFLAGS=\"{rustflags}\" CARGO_BUILD_INCREMENTAL=true {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} --release && (rm {raytracer_cg_clif_opt} || true) && ln {target_dir}/release/main {raytracer_cg_clif_opt}",
cargo_clif = cargo_clif.display(),
rustc_clif = rustc_clif.display(),
manifest_path = manifest_path.display(),
@@ -92,20 +111,14 @@ pub(crate) fn benchmark(dirs: &Dirs, compiler: &Compiler) {
let bench_run_markdown = dirs.build_dir.join("bench_run.md");
let raytracer_cg_llvm =
Path::new(".").join(get_file_name(&compiler.rustc, "raytracer_cg_llvm", "bin"));
let raytracer_cg_clif =
Path::new(".").join(get_file_name(&compiler.rustc, "raytracer_cg_clif", "bin"));
let raytracer_cg_clif_opt =
Path::new(".").join(get_file_name(&compiler.rustc, "raytracer_cg_clif_opt", "bin"));
let mut bench_run = hyperfine_command(
0,
bench_runs,
None,
&[
("", raytracer_cg_llvm.to_str().unwrap()),
("", raytracer_cg_clif.to_str().unwrap()),
("", raytracer_cg_clif_opt.to_str().unwrap()),
("build/raytracer_cg_llvm", &raytracer_cg_llvm),
("build/raytracer_cg_clif", &raytracer_cg_clif),
("build/raytracer_cg_clif_opt", &raytracer_cg_clif_opt),
],
&bench_run_markdown,
);
@@ -6,7 +6,7 @@
use crate::shared_utils::{rustflags_from_env, rustflags_to_cmd_env};
use crate::utils::{CargoProject, Compiler, LogGroup};
static CG_CLIF: CargoProject = CargoProject::new(&RelPath::source("."), "cg_clif");
static CG_CLIF: CargoProject = CargoProject::new(RelPath::source("."), "cg_clif");
pub(crate) fn build_backend(
dirs: &Dirs,
@@ -22,6 +22,11 @@ pub(crate) fn build_backend(
rustflags.push("-Zallow-features=rustc_private,f16,f128".to_owned());
rustflags_to_cmd_env(&mut cmd, "RUSTFLAGS", &rustflags);
// Use incr comp despite release mode unless incremental builds are explicitly disabled
if env::var_os("CARGO_BUILD_INCREMENTAL").is_none() {
cmd.env("CARGO_BUILD_INCREMENTAL", "true");
}
if env::var("CG_CLIF_EXPENSIVE_CHECKS").is_ok() {
// Enabling debug assertions implicitly enables the clif ir verifier
cmd.env("CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS", "true");
@@ -140,7 +140,7 @@ fn install_into_sysroot(&self, sysroot: &Path) {
static STDLIB_SRC: RelPath = RelPath::build("stdlib");
static STANDARD_LIBRARY: CargoProject =
CargoProject::new(&RelPath::build("stdlib/library/sysroot"), "stdlib_target");
CargoProject::new(RelPath::build("stdlib/library/sysroot"), "stdlib_target");
fn build_sysroot_for_triple(
dirs: &Dirs,
@@ -251,6 +251,10 @@ fn build_clif_sysroot_for_triple(
if compiler.triple.contains("apple") {
build_cmd.env("CARGO_PROFILE_RELEASE_SPLIT_DEBUGINFO", "packed");
}
// Use incr comp despite release mode unless incremental builds are explicitly disabled
if env::var_os("CARGO_BUILD_INCREMENTAL").is_none() {
build_cmd.env("CARGO_BUILD_INCREMENTAL", "true");
}
spawn_and_wait(build_cmd);
for entry in fs::read_dir(build_dir.join("deps")).unwrap() {
@@ -59,11 +59,6 @@ fn main() {
}
env::set_var("CG_CLIF_DISABLE_INCR_CACHE", "1");
// Force incr comp even in release mode unless in CI or incremental builds are explicitly disabled
if env::var_os("CARGO_BUILD_INCREMENTAL").is_none() {
env::set_var("CARGO_BUILD_INCREMENTAL", "true");
}
let mut args = env::args().skip(1);
let command = match args.next().as_deref() {
Some("prepare") => Command::Prepare,
@@ -79,7 +74,7 @@ fn main() {
}
};
let mut out_dir = PathBuf::from(".");
let mut out_dir = std::env::current_dir().unwrap();
let mut download_dir = None;
let mut sysroot_kind = SysrootKind::Clif;
let mut use_unstable_features = true;

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