Merge ref 'ce63e5d9ea' from rust-lang/rust

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

Upstream ref: ce63e5d9ea
Filtered ref: 3be97edf085b31c6544c8afec49edf22b7f04c2f
Upstream diff: dfe1b8c97b...ce63e5d9ea

This merge was created using https://github.com/rust-lang/josh-sync.
This commit is contained in:
The rustc-josh-sync Cronjob Bot 2025-12-13 13:33:29 +00:00
commit cf9c20b7a5
2410 changed files with 45256 additions and 30462 deletions

1
.github/FUNDING.yml vendored Normal file
View file

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

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

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

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>

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

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)
===========================

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)]

View file

@ -141,16 +141,11 @@ impl Path {
/// Check if this path is potentially a trivial const arg, i.e., one that can _potentially_
/// be represented without an anon const in the HIR.
///
/// 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 @@ impl Expr {
// then type of result is trait object.
// Otherwise we don't assume the result type.
ExprKind::Binary(binop, lhs, rhs) if binop.node == BinOpKind::Add => {
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.

View file

@ -13,7 +13,9 @@ use crate::ast::{
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 @@ impl AttributeExt for Attribute {
}
/// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
/// * `///doc` returns `Some(("doc", 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 @@ impl AttributeExt for Attribute {
fn is_automatically_derived_attr(&self) -> bool {
self.has_name(sym::automatically_derived)
}
fn is_doc_hidden(&self) -> bool {
self.has_name(sym::doc)
&& self.meta_item_list().is_some_and(|l| list_contains_name(&l, sym::hidden))
}
fn is_doc_keyword_or_attribute(&self) -> bool {
if self.has_name(sym::doc)
&& let Some(items) = self.meta_item_list()
{
for item in items {
if item.has_name(sym::keyword) || item.has_name(sym::attribute) {
return true;
}
}
}
false
}
}
impl Attribute {
@ -300,6 +326,25 @@ impl AttrItem {
}
}
/// Returns the span in:
///
/// ```text
/// #[attribute = "value"]
/// ^^^^^^^
/// ```
///
/// It returns `None` in any other cases like:
///
/// ```text
/// #[attr("value")]
/// ```
fn value_span(&self) -> Option<Span> {
match &self.args {
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 @@ impl 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 @@ pub trait AttributeExt: Debug {
/// * `/** 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 @@ pub trait AttributeExt: Debug {
/// commented module (for inner doc) vs within its parent module (for outer
/// doc).
fn doc_resolution_scope(&self) -> Option<AttrStyle>;
/// Returns `true` if this attribute contains `doc(hidden)`.
fn is_doc_hidden(&self) -> bool;
/// Returns `true` is this attribute contains `doc(keyword)` or `doc(attribute)`.
fn is_doc_keyword_or_attribute(&self) -> bool;
}
// FIXME(fn_delegation): use function delegation instead of manually forwarding
@ -902,7 +950,7 @@ impl Attribute {
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)
}
}

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)]

View file

@ -16,7 +16,31 @@ use rustc_span::{Ident, Symbol};
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,

View file

@ -415,6 +415,7 @@ macro_rules! common_visitor_and_walkers {
UnsafeBinderCastKind,
BinOpKind,
BlockCheckMode,
MgcaDisambiguation,
BorrowKind,
BoundAsyncness,
BoundConstness,
@ -1048,8 +1049,8 @@ macro_rules! common_visitor_and_walkers {
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) =>

View file

@ -23,7 +23,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// The order in which things are lowered is important! I.e to
// refer to variables in contract_decls from postcond/precond,
// we must lower it first!
let contract_decls = self.lower_stmts(&contract.declarations).0;
let contract_decls = self.lower_decls(contract);
match (&contract.requires, &contract.ensures) {
(Some(req), Some(ens)) => {
@ -124,6 +124,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
fn lower_decls(&mut self, contract: &rustc_ast::FnContract) -> &'hir [rustc_hir::Stmt<'hir>] {
let (decls, decls_tail) = self.lower_stmts(&contract.declarations);
if let Some(e) = decls_tail {
// include the tail expression in the declaration statements
let tail = self.stmt_expr(e.span, *e);
self.arena.alloc_from_iter(decls.into_iter().map(|d| *d).chain([tail].into_iter()))
} else {
decls
}
}
/// Lower the precondition check intrinsic.
fn lower_precond(&mut self, req: &Box<rustc_ast::Expr>) -> rustc_hir::Stmt<'hir> {
let lowered_req = self.lower_expr_mut(&req);

View file

@ -43,12 +43,15 @@ use hir::def::{DefKind, PartialRes, Res};
use hir::{BodyId, HirId};
use rustc_abi::ExternAbi;
use rustc_ast::*;
use rustc_attr_parsing::{AttributeParser, ShouldEmit};
use rustc_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 @@ impl<'hir> LoweringContext<'_, 'hir> {
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 @@ impl<'hir> LoweringContext<'_, 'hir> {
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 @@ impl<'hir> LoweringContext<'_, 'hir> {
// We are not forwarding the attributes, as the delegation fn sigs are collected on the ast,
// and here we need the hir attributes.
let default_safety =
if sig.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 @@ impl<'hir> LoweringContext<'_, 'hir> {
&mut self,
err: ErrorGuaranteed,
span: Span,
delegation: &Delegation,
) -> DelegationResults<'hir> {
let generics = self.lower_delegation_generics(span);
@ -418,8 +557,41 @@ impl<'hir> LoweringContext<'_, 'hir> {
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 }
}

View file

@ -1,3 +1,4 @@
use std::mem;
use std::ops::ControlFlow;
use std::sync::Arc;
@ -27,7 +28,9 @@ use super::{
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 @@ impl<'hir> LoweringContext<'_, '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 @@ impl<'hir> LoweringContext<'_, 'hir> {
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 @@ impl<'hir> LoweringContext<'_, 'hir> {
/// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_output(<expr>) }`,
/// `try { <stmts>; }` into `{ <stmts>; ::std::ops::Try::from_output(()) }`
/// and save the block id to use it as a break target for desugaring of the `?` operator.
fn lower_expr_try_block(&mut self, body: &Block) -> 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 @@ impl<'hir> LoweringContext<'_, '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 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
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 @@ impl<'hir> LoweringContext<'_, 'hir> {
let residual_ident = Ident::with_dummy_span(sym::residual);
let (residual_local, residual_local_nid) = self.pat_ident(try_span, residual_ident);
let residual_expr = self.expr_ident_mut(try_span, residual_ident, residual_local_nid);
let (constructor_item, target_id) = match self.try_block_scope {
TryBlockScope::Function => {
(hir::LangItem::TryTraitFromResidual, Err(hir::LoopIdError::OutsideLoopScope))
}
TryBlockScope::Homogeneous(block_id) => {
(hir::LangItem::ResidualIntoTryType, Ok(block_id))
}
TryBlockScope::Heterogeneous(block_id) => {
(hir::LangItem::TryTraitFromResidual, Ok(block_id))
}
};
let from_residual_expr = self.wrap_in_try_constructor(
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 @@ impl<'hir> LoweringContext<'_, 'hir> {
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)),
}
}

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 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
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 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
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;
@ -1206,7 +1219,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.and_then(|partial_res| partial_res.full_res())
{
if !res.matches_ns(Namespace::TypeNS)
&& path.is_potential_trivial_const_arg(false)
&& path.is_potential_trivial_const_arg()
{
debug!(
"lower_generic_arg: Lowering type argument as const argument: {:?}",
@ -1670,7 +1683,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
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 +1768,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn_def_id: LocalDefId,
coro: CoroutineKind,
fn_kind: FnDeclKind,
fn_span: Span,
) -> hir::FnRetTy<'hir> {
let span = self.lower_span(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 +2287,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
) -> &'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 +2368,53 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
#[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 +2424,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
#[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 +2451,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} else {
&anon.value
};
let maybe_res =
self.resolver.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res());
if let ExprKind::Path(qself, path) = &expr.kind
&& path.is_potential_trivial_const_arg(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 +2464,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
path,
ParamMode::Explicit,
AllowReturnTypeNotation::No,
// FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);

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

View file

@ -33,7 +33,7 @@ use rustc_session::Session;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::{
DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN,
PATTERNS_IN_FNS_WITHOUT_BODY,
PATTERNS_IN_FNS_WITHOUT_BODY, UNUSED_VISIBILITIES,
};
use rustc_session::parse::feature_err;
use rustc_span::{Ident, Span, kw, sym};
@ -710,6 +710,14 @@ impl<'a> AstValidator<'a> {
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) {
@ -1331,7 +1339,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
});
}
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 +1347,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
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, .. }) => {

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`)

View file

@ -301,17 +301,12 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
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 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
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 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
gate_all!(postfix_match, "postfix match is experimental");
gate_all!(mut_ref, "mutable by-reference bindings are experimental");
gate_all!(min_generic_const_args, "unbraced const blocks as const args are experimental");
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");

View file

@ -10,7 +10,7 @@ use std::borrow::Cow;
use std::sync::Arc;
use rustc_ast::attr::AttrIdGenerator;
use rustc_ast::token::{self, CommentKind, Delimiter, 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 space_between(tt1: &TokenTree, tt2: &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 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
self.word("]");
}
ast::AttrKind::DocComment(comment_kind, data) => {
self.word(doc_comment_to_string(*comment_kind, attr.style, *data));
self.word(doc_comment_to_string(
DocFragmentKind::Sugared(*comment_kind),
attr.style,
*data,
));
self.hardbreak()
}
}
@ -1029,7 +1042,8 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
/* Other */
token::DocComment(comment_kind, attr_style, data) => {
doc_comment_to_string(comment_kind, attr_style, data).into()
doc_comment_to_string(DocFragmentKind::Sugared(comment_kind), attr_style, data)
.into()
}
token::Eof => "<eof>".into(),
}

View file

@ -818,10 +818,15 @@ impl<'a> State<'a> {
);
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) => {

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

View file

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

View file

@ -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 @@ use crate::session_diagnostics::{
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 @@ const CFG_ATTR_TEMPLATE: AttributeTemplate = template!(
"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();
}
}

View file

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

View file

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

View file

@ -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 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
}
}
fn parse_tf_attribute<'c, S: Stage>(
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) -> impl IntoIterator<Item = (Symbol, Span)> + 'c {
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;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
}
}
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) => {

View file

@ -2,6 +2,7 @@ use rustc_feature::Features;
use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection};
use rustc_hir::attrs::*;
use rustc_session::Session;
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
use rustc_session::parse::feature_err;
use rustc_span::kw;
use rustc_target::spec::{Arch, BinaryFormat};
@ -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 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
impl LinkParser {
fn parse_link_name<S: Stage>(
item: &MetaItemParser<'_>,
item: &MetaItemParser,
name: &mut Option<(Symbol, Span)>,
cx: &mut AcceptContext<'_, '_, S>,
) -> bool {
@ -268,7 +267,7 @@ impl LinkParser {
}
fn parse_link_kind<S: Stage>(
item: &MetaItemParser<'_>,
item: &MetaItemParser,
kind: &mut Option<NativeLibKind>,
cx: &mut AcceptContext<'_, '_, S>,
sess: &Session,
@ -348,7 +347,7 @@ impl LinkParser {
}
fn parse_link_modifiers<S: Stage>(
item: &MetaItemParser<'_>,
item: &MetaItemParser,
modifiers: &mut Option<(Symbol, Span)>,
cx: &mut AcceptContext<'_, '_, S>,
) -> bool {
@ -369,7 +368,7 @@ impl LinkParser {
}
fn parse_link_cfg<S: Stage>(
item: &MetaItemParser<'_>,
item: &MetaItemParser,
cfg: &mut Option<CfgEntry>,
cx: &mut AcceptContext<'_, '_, S>,
sess: &Session,
@ -401,7 +400,7 @@ impl LinkParser {
}
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 @@ impl LinkParser {
}
fn parse_link_import_name_type<S: Stage>(
item: &MetaItemParser<'_>,
item: &MetaItemParser,
import_name_type: &mut Option<(PeImportNameType, Span)>,
cx: &mut AcceptContext<'_, '_, S>,
) -> bool {
@ -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;

View file

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

View file

@ -32,13 +32,13 @@ mod prelude;
pub(crate) mod allow_unstable;
pub(crate) mod body;
pub(crate) mod cfg;
pub(crate) mod cfg_old;
pub(crate) mod cfg_select;
pub(crate) mod 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 traits;
pub(crate) mod transparency;
pub(crate) mod util;
type AcceptFn<T, S> = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser<'_>);
type AcceptFn<T, S> = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess, S>, &ArgParser);
type AcceptMapping<T, S> = &'static [(&'static [Symbol], AttributeTemplate, AcceptFn<T, S>)];
/// An [`AttributeParser`] is a type which searches for syntactic attributes.
@ -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`].

View file

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

View file

@ -13,7 +13,7 @@ impl<S: Stage> SingleAttributeParser<S> for PathParser {
"https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute"
);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(nv) = args.name_value() else {
cx.expected_name_value(cx.attr_span, None);
return None;

View file

@ -4,8 +4,6 @@ pub(super) use rustc_feature::{AttributeTemplate, template};
#[doc(hidden)]
pub(super) use rustc_hir::attrs::AttributeKind;
#[doc(hidden)]
pub(super) use rustc_hir::lints::AttributeLintKind;
#[doc(hidden)]
pub(super) use rustc_hir::{MethodKind, Target};
#[doc(hidden)]
pub(super) use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};

View file

@ -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 @@ impl<S: Stage> SingleAttributeParser<S> for RustcBuiltinMacroParser {
fn parse_derive_like<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser<'_>,
args: &ArgParser,
trait_name_mandatory: bool,
) -> Option<(Option<Symbol>, ThinVec<Symbol>)> {
let Some(list) = args.list() else {

View file

@ -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 @@ impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
fn extract_value<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
key: Symbol,
arg: &ArgParser<'_>,
arg: &ArgParser,
span: Span,
out_val: &mut Option<(Symbol, Span)>,
failed: &mut bool,

View file

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

View file

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

View file

@ -8,7 +8,7 @@ use rustc_hir::{
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 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
/// `name = value`
fn insert_value_into_option_or_error<S: Stage>(
cx: &AcceptContext<'_, '_, S>,
param: &MetaItemParser<'_>,
param: &MetaItemParser,
item: &mut Option<Symbol>,
name: Ident,
) -> Option<()> {
@ -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;
};

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@ use rustc_ast::attr::AttributeExt;
use rustc_feature::is_builtin_attr_name;
use rustc_hir::RustcVersion;
use rustc_hir::limit::Limit;
use rustc_span::{Symbol, 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);

View file

@ -11,6 +11,7 @@ use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
use rustc_hir::{AttrPath, CRATE_HIR_ID, HirId};
use rustc_session::Session;
use rustc_session::lint::{Lint, LintId};
use rustc_span::{ErrorGuaranteed, Span, Symbol};
use crate::AttributeParser;
@ -32,6 +33,7 @@ use crate::attributes::crate_level::{
};
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::traits::{
};
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 @@ attribute_parsers!(
BodyStabilityParser,
ConfusablesParser,
ConstStabilityParser,
DocParser,
MacroUseParser,
NakedParser,
StabilityParser,
@ -381,7 +384,7 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
/// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing
/// must be delayed until after HIR is built. This method will take care of the details of
/// that.
pub(crate) fn emit_lint(&mut self, lint: 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 +392,12 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
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 +413,7 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
unused_span: Span,
) {
self.emit_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
AttributeLintKind::UnusedDuplicate {
this: unused_span,
other: used_span,
@ -424,7 +429,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
&self,
span: Span,
found: String,
options: &'static [&'static str],
options: &[&'static str],
) -> ErrorGuaranteed {
self.emit_err(UnknownMetaItem { span, item: found, expected: options })
}
@ -632,14 +637,25 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
}
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 +713,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> {

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 @@ impl<'sess> AttributeParser<'sess, Early> {
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 @@ impl<'sess> AttributeParser<'sess, Early> {
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 @@ impl<'sess> AttributeParser<'sess, Early> {
};
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 @@ impl<'sess> AttributeParser<'sess, Early> {
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 @@ impl<'sess> AttributeParser<'sess, Early> {
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 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
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 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
// that's expanded right? But no, sometimes, when parsing attributes on macros,
// we already use the lowering logic and these are still there. So, when `omit_doc`
// is set we *also* want to ignore these.
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 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
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 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
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 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
// );
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,

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 @@ mod interface;
/// 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 mod validate_attr;
pub use attributes::cfg::{
CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg, parse_cfg_attr, parse_cfg_entry,
};
pub use attributes::cfg_old::*;
pub use attributes::cfg_select::*;
pub use attributes::util::{is_builtin_attr, 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" }

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

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 @@ use crate::session_diagnostics::{
};
#[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 @@ impl<'a> PathParser<'a> {
}
}
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 @@ impl<'a> PathParser<'a> {
}
}
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 @@ impl<'a> ArgParser<'a> {
}
pub fn from_attr_args<'sess>(
value: &'a AttrArgs,
value: &AttrArgs,
parts: &[Symbol],
psess: &'sess ParseSess,
should_emit: ShouldEmit,
@ -144,7 +147,7 @@ impl<'a> ArgParser<'a> {
///
/// - `#[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 @@ impl<'a> ArgParser<'a> {
///
/// 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 @@ impl<'sess> MetaItemOrLitParser<'sess> {
}
}
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 @@ impl<'sess> MetaItemOrLitParser<'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 @@ impl<'a> Debug for MetaItemParser<'a> {
}
}
impl<'a> MetaItemParser<'a> {
/// Create a new parser from a [`NormalAttr`], which is stored inside of any
/// [`ast::Attribute`](rustc_ast::Attribute)
pub fn from_attr<'sess>(
attr: &'a NormalAttr,
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 @@ impl<'a> MetaItemParser<'a> {
/// - `#[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 @@ impl<'a> MetaItemParser<'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 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
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 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
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 @@ impl<'a, 'sess> MetaItemListParserContext<'a, '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 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
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 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
}
#[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 @@ impl<'a> MetaItemListParser<'a> {
}
/// 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 @@ impl<'a> MetaItemListParser<'a> {
/// 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())
}

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",
);
}
}
}
}

View file

@ -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 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
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,
}

View file

@ -5,6 +5,7 @@ use rustc_errors::DiagArgValue;
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 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
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 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
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);
}
}

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

View file

@ -52,8 +52,8 @@ impl<'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 @@ pub(crate) trait TypeOpInfo<'tcx> {
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 @@ pub(crate) trait TypeOpInfo<'tcx> {
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() {

View file

@ -561,11 +561,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
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) => {

View file

@ -410,6 +410,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
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 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
// 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() {

View file

@ -1124,16 +1124,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
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.

View file

@ -142,12 +142,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
} 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] } => {

View file

@ -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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, '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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
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 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
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);

View file

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

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 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
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 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
#[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

View file

@ -67,12 +67,11 @@ impl<'a, 'tcx> Visitor<'tcx> for AccessFactsExtractor<'a, 'tcx> {
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)));

View file

@ -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 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for VarianceExtractor<'_, '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,

View file

@ -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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// 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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
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 @@ impl<'tcx> RegionInferenceContext<'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 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// 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

View file

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

View file

@ -10,8 +10,8 @@ use rustc_middle::ty::{self, RegionVid};
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 @@ rustc_index::newtype_index! {
/// 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 @@ impl LivenessValues {
/// 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 @@ impl PlaceholderIndices {
/// 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 @@ impl<N: Idx> RegionValues<N> {
/// 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 @@ impl<N: Idx> RegionValues<N> {
}
/// 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 @@ impl<N: Idx> RegionValues<N> {
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 @@ impl<N: Idx> RegionValues<N> {
}
/// 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 @@ impl<N: Idx> RegionValues<N> {
}
}
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('{');

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 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
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 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
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 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
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 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
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 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
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 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
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 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
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 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
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 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
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 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
*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 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
*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 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
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 => {

View file

@ -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_infer::traits::solve::Goal;
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 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
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 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
}
#[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 @@ impl<'b, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'b, '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 @@ impl<'b, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'b, '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 {

View file

@ -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 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, '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 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, '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 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, '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 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, '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 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, '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 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, '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 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, '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 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, '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

View file

@ -315,7 +315,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
| ExprKind::Path(_, _)
| ExprKind::Ret(_)
| ExprKind::Try(_)
| ExprKind::TryBlock(_)
| ExprKind::TryBlock(_, _)
| ExprKind::Type(_, _)
| ExprKind::Underscore
| ExprKind::While(_, _, _)

View file

@ -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};
@ -558,7 +558,11 @@ mod llvm_enzyme {
}
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 +817,7 @@ mod llvm_enzyme {
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 +832,7 @@ mod llvm_enzyme {
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 =

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,

View file

@ -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 @@ use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable};
/// 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>(

View file

@ -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) => {

View file

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

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

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

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

View file

@ -11,7 +11,7 @@ static ABI_CAFE_REPO: GitRepo = GitRepo::github(
"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,

View file

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

View file

@ -6,7 +6,7 @@ use crate::rustc_info::get_file_name;
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");

View file

@ -140,7 +140,7 @@ impl SysrootTarget {
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() {

View file

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

View file

@ -129,7 +129,7 @@ pub(crate) static RAND_REPO: GitRepo = GitRepo::github(
"rand",
);
static RAND: CargoProject = CargoProject::new(&RAND_REPO.source_dir(), "rand_target");
static RAND: CargoProject = CargoProject::new(RAND_REPO.source_dir(), "rand_target");
pub(crate) static REGEX_REPO: GitRepo = GitRepo::github(
"rust-lang",
@ -139,15 +139,15 @@ pub(crate) static REGEX_REPO: GitRepo = GitRepo::github(
"regex",
);
static REGEX: CargoProject = CargoProject::new(&REGEX_REPO.source_dir(), "regex_target");
static REGEX: CargoProject = CargoProject::new(REGEX_REPO.source_dir(), "regex_target");
static PORTABLE_SIMD_SRC: RelPath = RelPath::build("portable-simd");
static PORTABLE_SIMD: CargoProject = CargoProject::new(&PORTABLE_SIMD_SRC, "portable-simd_target");
static PORTABLE_SIMD: CargoProject = CargoProject::new(PORTABLE_SIMD_SRC, "portable-simd_target");
static SYSROOT_TESTS_SRC: RelPath = RelPath::build("sysroot_tests");
static SYSROOT_TESTS: CargoProject = CargoProject::new(&SYSROOT_TESTS_SRC, "sysroot_tests_target");
static SYSROOT_TESTS: CargoProject = CargoProject::new(SYSROOT_TESTS_SRC, "sysroot_tests_target");
const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[
TestCase::custom("test.rust-random/rand", &|runner| {

View file

@ -75,12 +75,12 @@ impl Compiler {
}
pub(crate) struct CargoProject {
source: &'static RelPath,
source: RelPath,
target: &'static str,
}
impl CargoProject {
pub(crate) const fn new(path: &'static RelPath, target: &'static str) -> CargoProject {
pub(crate) const fn new(path: RelPath, target: &'static str) -> CargoProject {
CargoProject { source: path, target }
}

View file

@ -1,26 +0,0 @@
From 5489384bc265e9e6fc2efaa63d93a4d51ebec2f5 Mon Sep 17 00:00:00 2001
From: bjorn3 <17426603+bjorn3@users.noreply.github.com>
Date: Thu, 22 Aug 2024 19:22:58 +0000
Subject: [PATCH] Disable broken reduce_sum test
It was broken by an upstream change to the .sum() implementation on
float iterators.
---
crates/core_simd/tests/ops_macros.rs | 1 +
1 file changed, 1 insertion(+)
diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs
index aa565a1..5e6ac41 100644
--- a/crates/core_simd/tests/ops_macros.rs
+++ b/crates/core_simd/tests/ops_macros.rs
@@ -646,6 +646,7 @@ macro_rules! impl_float_tests {
}
fn reduce_sum<const LANES: usize>() {
+ return;
test_helpers::test_1(&|x| {
test_helpers::prop_assert_biteq! (
Vector::<LANES>::from_array(x).reduce_sum(),
--
2.34.1

View file

@ -1,4 +1,4 @@
[toolchain]
channel = "nightly-2025-11-08"
channel = "nightly-2025-12-08"
components = ["rust-src", "rustc-dev", "llvm-tools"]
profile = "minimal"

View file

@ -43,7 +43,7 @@ case $1 in
cg_clif=$(pwd)
pushd ../rust
git pull origin master
git pull origin main
branch=sync_cg_clif-$(date +%Y-%m-%d)
git checkout -b "$branch"
"$cg_clif/git-fixed-subtree.sh" pull --prefix=compiler/rustc_codegen_cranelift/ https://github.com/rust-lang/rustc_codegen_cranelift.git main
@ -63,7 +63,7 @@ case $1 in
cg_clif=$(pwd)
pushd ../rust
git fetch origin master
git fetch origin main
git -c advice.detachedHead=false checkout "$RUST_VERS"
"$cg_clif/git-fixed-subtree.sh" push --prefix=compiler/rustc_codegen_cranelift/ "$cg_clif" sync_from_rust
popd

View file

@ -6,7 +6,6 @@ use rustc_ast::expand::allocator::{
AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, default_fn_name, global_fn_name,
};
use rustc_codegen_ssa::base::{allocator_kind_for_codegen, allocator_shim_contents};
use rustc_session::config::OomStrategy;
use rustc_symbol_mangling::mangle_internal_symbol;
use crate::prelude::*;
@ -15,16 +14,11 @@ use crate::prelude::*;
pub(crate) fn codegen(tcx: TyCtxt<'_>, module: &mut dyn Module) -> bool {
let Some(kind) = allocator_kind_for_codegen(tcx) else { return false };
let methods = allocator_shim_contents(tcx, kind);
codegen_inner(tcx, module, &methods, tcx.sess.opts.unstable_opts.oom);
codegen_inner(tcx, module, &methods);
true
}
fn codegen_inner(
tcx: TyCtxt<'_>,
module: &mut dyn Module,
methods: &[AllocatorMethod],
oom_strategy: OomStrategy,
) {
fn codegen_inner(tcx: TyCtxt<'_>, module: &mut dyn Module, methods: &[AllocatorMethod]) {
let usize_ty = module.target_config().pointer_type();
for method in methods {
@ -65,35 +59,6 @@ fn codegen_inner(
);
}
{
let sig = Signature {
call_conv: module.target_config().default_call_conv,
params: vec![],
returns: vec![AbiParam::new(types::I8)],
};
let func_id = module
.declare_function(
&mangle_internal_symbol(tcx, OomStrategy::SYMBOL),
Linkage::Export,
&sig,
)
.unwrap();
let mut ctx = Context::new();
ctx.func.signature = sig;
{
let mut func_ctx = FunctionBuilderContext::new();
let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
let block = bcx.create_block();
bcx.switch_to_block(block);
let value = bcx.ins().iconst(types::I8, oom_strategy.should_panic() as i64);
bcx.ins().return_(&[value]);
bcx.seal_all_blocks();
bcx.finalize();
}
module.define_function(func_id, &mut ctx).unwrap();
}
{
let sig = Signature {
call_conv: module.target_config().default_call_conv,

View file

@ -167,7 +167,7 @@ pub(crate) fn compile_fn(
context.clear();
context.func = codegened_func.func;
#[cfg(any())] // This is never true
#[cfg(false)]
let _clif_guard = {
use std::fmt::Write;
@ -689,7 +689,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
lval.write_cvalue(fx, res);
}
Rvalue::Cast(
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _),
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _),
ref operand,
to_ty,
) => {

View file

@ -377,26 +377,28 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
size.is_multiple_of(align),
"size must be a multiple of alignment (size={size}, align={align})"
);
debug_assert!(align.is_power_of_two(), "alignment must be a power of two (align={align})");
let abi_align = if self.tcx.sess.target.arch == Arch::S390x { 8 } else { 16 };
// Cranelift can only guarantee alignment up to the ABI alignment provided by the target.
// If the requested alignment is less than the abi_align it can be used directly.
if align <= abi_align {
let stack_slot = self.bcx.create_sized_stack_slot(StackSlotData {
kind: StackSlotKind::ExplicitSlot,
// FIXME Don't force the size to a multiple of <abi_align> bytes once Cranelift gets
// a way to specify stack slot alignment.
size: size.div_ceil(abi_align) * abi_align,
align_shift: 4,
size,
// The maximum value of ilog2 is 31 which will always fit in a u8.
align_shift: align.ilog2().try_into().unwrap(),
key: None,
});
Pointer::stack_slot(stack_slot)
} else {
// Alignment is too big to handle using the above hack. Dynamically realign a stack slot
// Alignment is larger than the ABI alignment guaranteed. Dynamically realign a stack slot
// instead. This wastes some space for the realignment.
let stack_slot = self.bcx.create_sized_stack_slot(StackSlotData {
kind: StackSlotKind::ExplicitSlot,
// FIXME Don't force the size to a multiple of <abi_align> bytes once Cranelift gets
// a way to specify stack slot alignment.
size: (size + align) / abi_align * abi_align,
align_shift: 4,
size: size + align,
align_shift: abi_align.ilog2().try_into().unwrap(),
key: None,
});
let base_ptr = self.bcx.ins().stack_addr(self.pointer_type, stack_slot, 0);
let misalign_offset = self.bcx.ins().band_imm(base_ptr, i64::from(align - 1));

View file

@ -5,7 +5,7 @@ use std::path::{Component, Path};
use cranelift_codegen::MachSrcLoc;
use cranelift_codegen::binemit::CodeOffset;
use gimli::write::{AttributeValue, FileId, FileInfo, LineProgram, LineString, LineStringTable};
use gimli::write::{FileId, FileInfo, LineProgram, LineString, LineStringTable};
use rustc_span::{FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHashAlgorithm, hygiene};
use crate::debuginfo::FunctionDebugContext;
@ -117,8 +117,7 @@ impl DebugContext {
}
filename => {
// For anonymous sources, create an empty directory instead of using the default
let empty_dir = LineString::new(b"", line_program.encoding(), line_strings);
let dir_id = line_program.add_directory(empty_dir);
let dir_id = line_program.default_directory();
let dummy_file_name = LineString::new(
filename.prefer_remapped_unconditionally().to_string().into_bytes(),
@ -176,10 +175,6 @@ impl FunctionDebugContext {
assert_ne!(func_end, 0);
let entry = debug_context.dwarf.unit.get_mut(self.entry_id);
entry.set(gimli::DW_AT_low_pc, AttributeValue::Address(address_for_func(func_id)));
entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(func_end)));
func_end
}
}

View file

@ -42,14 +42,14 @@ pub(crate) struct DebugContext {
created_files: FxHashMap<(StableSourceFileId, SourceFileHash), FileId>,
stack_pointer_register: Register,
namespace_map: DefIdMap<UnitEntryId>,
array_size_type: UnitEntryId,
array_size_type: Option<UnitEntryId>,
filename_display_preference: FileNameDisplayPreference,
embed_source: bool,
}
pub(crate) struct FunctionDebugContext {
entry_id: UnitEntryId,
entry_id: Option<UnitEntryId>,
function_source_loc: (FileId, u64, u64),
source_loc_set: IndexSet<(FileId, u64, u64)>,
}
@ -154,18 +154,23 @@ impl DebugContext {
root.set(gimli::DW_AT_low_pc, AttributeValue::Address(Address::Constant(0)));
}
let array_size_type = dwarf.unit.add(dwarf.unit.root(), gimli::DW_TAG_base_type);
let array_size_type_entry = dwarf.unit.get_mut(array_size_type);
array_size_type_entry.set(
gimli::DW_AT_name,
AttributeValue::StringRef(dwarf.strings.add("__ARRAY_SIZE_TYPE__")),
);
array_size_type_entry
.set(gimli::DW_AT_encoding, AttributeValue::Encoding(gimli::DW_ATE_unsigned));
array_size_type_entry.set(
gimli::DW_AT_byte_size,
AttributeValue::Udata(isa.frontend_config().pointer_bytes().into()),
);
let array_size_type = if tcx.sess.opts.debuginfo == DebugInfo::LineTablesOnly {
None
} else {
let array_size_type = dwarf.unit.add(dwarf.unit.root(), gimli::DW_TAG_base_type);
let array_size_type_entry = dwarf.unit.get_mut(array_size_type);
array_size_type_entry.set(
gimli::DW_AT_name,
AttributeValue::StringRef(dwarf.strings.add("__ARRAY_SIZE_TYPE__")),
);
array_size_type_entry
.set(gimli::DW_AT_encoding, AttributeValue::Encoding(gimli::DW_ATE_unsigned));
array_size_type_entry.set(
gimli::DW_AT_byte_size,
AttributeValue::Udata(isa.frontend_config().pointer_bytes().into()),
);
Some(array_size_type)
};
Some(DebugContext {
endian,
@ -217,6 +222,14 @@ impl DebugContext {
) -> FunctionDebugContext {
let (file_id, line, column) = self.get_span_loc(tcx, function_span, function_span);
if tcx.sess.opts.debuginfo == DebugInfo::LineTablesOnly {
return FunctionDebugContext {
entry_id: None,
function_source_loc: (file_id, line, column),
source_loc_set: IndexSet::new(),
};
}
let scope = self.item_namespace(tcx, tcx.parent(instance.def_id()));
let mut name = String::new();
@ -274,7 +287,7 @@ impl DebugContext {
}
FunctionDebugContext {
entry_id,
entry_id: Some(entry_id),
function_source_loc: (file_id, line, column),
source_loc_set: IndexSet::new(),
}
@ -288,6 +301,10 @@ impl DebugContext {
def_id: DefId,
data_id: DataId,
) {
if tcx.sess.opts.debuginfo == DebugInfo::LineTablesOnly {
return;
}
let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else { bug!() };
if nested {
return;
@ -353,10 +370,12 @@ impl FunctionDebugContext {
.0
.push(Range::StartLength { begin: address_for_func(func_id), length: u64::from(end) });
let func_entry = debug_context.dwarf.unit.get_mut(self.entry_id);
// Gdb requires both DW_AT_low_pc and DW_AT_high_pc. Otherwise the DW_TAG_subprogram is skipped.
func_entry.set(gimli::DW_AT_low_pc, AttributeValue::Address(address_for_func(func_id)));
// Using Udata for DW_AT_high_pc requires at least DWARF4
func_entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(end)));
if let Some(entry_id) = self.entry_id {
let entry = debug_context.dwarf.unit.get_mut(entry_id);
// Gdb requires both DW_AT_low_pc and DW_AT_high_pc. Otherwise the DW_TAG_subprogram is skipped.
entry.set(gimli::DW_AT_low_pc, AttributeValue::Address(address_for_func(func_id)));
// Using Udata for DW_AT_high_pc requires at least DWARF4
entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(end)));
}
}
}

View file

@ -109,7 +109,8 @@ impl DebugContext {
let subrange_id = self.dwarf.unit.add(array_type_id, gimli::DW_TAG_subrange_type);
let subrange_entry = self.dwarf.unit.get_mut(subrange_id);
subrange_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(self.array_size_type));
subrange_entry
.set(gimli::DW_AT_type, AttributeValue::UnitRef(self.array_size_type.unwrap()));
subrange_entry.set(gimli::DW_AT_lower_bound, AttributeValue::Udata(0));
subrange_entry.set(gimli::DW_AT_count, AttributeValue::Udata(len));

View file

@ -130,11 +130,9 @@ impl UnwindContext {
return;
}
let unwind_info = if let Some(unwind_info) =
let Some(unwind_info) =
context.compiled_code().unwrap().create_unwind_info(module.isa()).unwrap()
{
unwind_info
} else {
else {
return;
};

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