Introduce new-style attribute parsers for several attributes
note: compiler compiles but librustdoc and clippy don't
This commit is contained in:
parent
dbd3b7928e
commit
7e0f5b5016
50 changed files with 1519 additions and 1342 deletions
|
@ -3341,6 +3341,7 @@ dependencies = [
|
|||
"rustc_expand",
|
||||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_lexer",
|
||||
"rustc_lint_defs",
|
||||
|
@ -3597,6 +3598,7 @@ dependencies = [
|
|||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_ast_pretty",
|
||||
"rustc_attr_data_structures",
|
||||
"rustc_data_structures",
|
||||
"rustc_error_codes",
|
||||
"rustc_error_messages",
|
||||
|
@ -3631,6 +3633,7 @@ dependencies = [
|
|||
"rustc_errors",
|
||||
"rustc_feature",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_lexer",
|
||||
"rustc_lint_defs",
|
||||
"rustc_macros",
|
||||
|
@ -4319,6 +4322,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"bitflags",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_data_structures",
|
||||
"rustc_hir",
|
||||
"rustc_middle",
|
||||
|
@ -4415,6 +4419,7 @@ dependencies = [
|
|||
"punycode",
|
||||
"rustc-demangle",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_hashes",
|
||||
|
|
|
@ -874,11 +874,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
|
||||
debug_assert_eq!(id.owner, self.current_hir_id_owner);
|
||||
let ret = self.arena.alloc_from_iter(lowered_attrs);
|
||||
debug_assert!(!ret.is_empty());
|
||||
|
||||
// this is possible if an item contained syntactical attribute,
|
||||
// but none of them parse succesfully or all of them were ignored
|
||||
// for not being built-in attributes at all. They could be remaining
|
||||
// unexpanded attributes used as markers in proc-macro derives for example.
|
||||
// This will have emitted some diagnostics for the misparse, but will then
|
||||
// not emit the attribute making the list empty.
|
||||
if ret.is_empty() {
|
||||
&[]
|
||||
} else {
|
||||
self.attrs.insert(id.local_id, ret);
|
||||
ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_attrs_vec(&self, attrs: &[Attribute], target_span: Span) -> Vec<hir::Attribute> {
|
||||
self.attribute_parser.parse_attribute_list(attrs, target_span, OmitDoc::Lower)
|
||||
|
|
|
@ -207,8 +207,6 @@ ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing
|
|||
|
||||
ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc}
|
||||
|
||||
ast_passes_stability_outside_std = stability attributes may not be used outside of the standard library
|
||||
|
||||
ast_passes_static_without_body =
|
||||
free static item without body
|
||||
.suggestion = provide a definition for the static
|
||||
|
|
|
@ -732,13 +732,6 @@ pub(crate) struct AssociatedSuggestion2 {
|
|||
pub potential_assoc: Ident,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_stability_outside_std, code = E0734)]
|
||||
pub(crate) struct StabilityOutsideStd {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_feature_on_non_nightly, code = E0554)]
|
||||
pub(crate) struct FeatureOnNonNightly {
|
||||
|
|
|
@ -178,18 +178,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit errors for non-staged-api crates.
|
||||
if !self.features.staged_api() {
|
||||
if attr.has_name(sym::unstable)
|
||||
|| attr.has_name(sym::stable)
|
||||
|| attr.has_name(sym::rustc_const_unstable)
|
||||
|| attr.has_name(sym::rustc_const_stable)
|
||||
|| attr.has_name(sym::rustc_default_body_unstable)
|
||||
{
|
||||
self.sess.dcx().emit_err(errors::StabilityOutsideStd { span: attr.span });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, i: &'a ast::Item) {
|
||||
|
|
|
@ -2,9 +2,11 @@ use rustc_abi::Align;
|
|||
use rustc_ast::token::CommentKind;
|
||||
use rustc_ast::{self as ast, AttrStyle};
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::RustcVersion;
|
||||
use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
|
||||
pub enum InlineAttr {
|
||||
|
@ -72,6 +74,8 @@ pub enum ReprAttr {
|
|||
ReprSimd,
|
||||
ReprTransparent,
|
||||
ReprAlign(Align),
|
||||
// this one is just so we can emit a lint for it
|
||||
ReprEmpty,
|
||||
}
|
||||
pub use ReprAttr::*;
|
||||
|
||||
|
@ -150,10 +154,44 @@ impl Deprecation {
|
|||
/// happen.
|
||||
///
|
||||
/// For more docs, look in [`rustc_attr`](https://doc.rust-lang.org/stable/nightly-rustc/rustc_attr/index.html)
|
||||
// FIXME(jdonszelmann): rename to AttributeKind once hir::AttributeKind is dissolved
|
||||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)]
|
||||
pub enum AttributeKind {
|
||||
// tidy-alphabetical-start
|
||||
DocComment { style: AttrStyle, kind: CommentKind, span: Span, comment: Symbol },
|
||||
AllowConstFnUnstable(ThinVec<Symbol>),
|
||||
AllowInternalUnstable(ThinVec<(Symbol, Span)>),
|
||||
BodyStability {
|
||||
stability: DefaultBodyStability,
|
||||
/// Span of the `#[rustc_default_body_unstable(...)]` attribute
|
||||
span: Span,
|
||||
},
|
||||
Confusables {
|
||||
symbols: ThinVec<Symbol>,
|
||||
// FIXME(jdonszelmann): remove when target validation code is moved
|
||||
first_span: Span,
|
||||
},
|
||||
ConstStability {
|
||||
stability: PartialConstStability,
|
||||
/// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute
|
||||
span: Span,
|
||||
},
|
||||
ConstStabilityIndirect,
|
||||
Deprecation {
|
||||
deprecation: Deprecation,
|
||||
span: Span,
|
||||
},
|
||||
Diagnostic(DiagnosticAttribute),
|
||||
DocComment {
|
||||
style: AttrStyle,
|
||||
kind: CommentKind,
|
||||
span: Span,
|
||||
comment: Symbol,
|
||||
},
|
||||
MacroTransparency(Transparency),
|
||||
Repr(ThinVec<(ReprAttr, Span)>),
|
||||
Stability {
|
||||
stability: Stability,
|
||||
/// Span of the `#[stable(...)]` or `#[unstable(...)]` attribute
|
||||
span: Span,
|
||||
},
|
||||
// tidy-alphabetical-end
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ attr_parsing_deprecated_item_suggestion =
|
|||
.help = add `#![feature(deprecated_suggestion)]` to the crate root
|
||||
.note = see #94785 for more details
|
||||
|
||||
attr_parsing_empty_confusables =
|
||||
expected at least one confusable name
|
||||
attr_parsing_expected_one_cfg_pattern =
|
||||
expected 1 cfg-pattern
|
||||
|
||||
|
@ -21,8 +23,8 @@ attr_parsing_expects_feature_list =
|
|||
attr_parsing_expects_features =
|
||||
`{$name}` expects feature names
|
||||
|
||||
attr_parsing_incorrect_meta_item =
|
||||
incorrect meta item
|
||||
attr_parsing_incorrect_meta_item = expected a quoted string literal
|
||||
attr_parsing_incorrect_meta_item_suggestion = consider surrounding this with quotes
|
||||
|
||||
attr_parsing_incorrect_repr_format_align_one_arg =
|
||||
incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
|
||||
|
@ -90,18 +92,18 @@ attr_parsing_non_ident_feature =
|
|||
|
||||
attr_parsing_repr_ident =
|
||||
meta item in `repr` must be an identifier
|
||||
|
||||
attr_parsing_rustc_allowed_unstable_pairing =
|
||||
`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
|
||||
|
||||
attr_parsing_rustc_const_stable_indirect_pairing =
|
||||
`const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied
|
||||
|
||||
attr_parsing_rustc_promotable_pairing =
|
||||
`rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute
|
||||
|
||||
attr_parsing_soft_no_args =
|
||||
`soft` should not have any arguments
|
||||
|
||||
attr_parsing_stability_outside_std = stability attributes may not be used outside of the standard library
|
||||
|
||||
attr_parsing_unknown_meta_item =
|
||||
unknown meta item '{$item}'
|
||||
.label = expected one of {$expected}
|
||||
|
@ -128,3 +130,8 @@ attr_parsing_unsupported_literal_generic =
|
|||
unsupported literal
|
||||
attr_parsing_unsupported_literal_suggestion =
|
||||
consider removing the prefix
|
||||
|
||||
attr_parsing_unused_multiple =
|
||||
multiple `{$name}` attributes
|
||||
.suggestion = remove this attribute
|
||||
.note = attribute also specified here
|
||||
|
|
|
@ -1,49 +1,67 @@
|
|||
use rustc_ast::attr::{AttributeExt, filter_by_name};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{Symbol, sym};
|
||||
use std::iter;
|
||||
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::{CombineAttributeParser, ConvertFn};
|
||||
use crate::context::AcceptContext;
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics;
|
||||
|
||||
pub fn allow_internal_unstable(
|
||||
sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
) -> impl Iterator<Item = Symbol> {
|
||||
allow_unstable(sess, attrs, sym::allow_internal_unstable)
|
||||
pub(crate) struct AllowInternalUnstableParser;
|
||||
impl CombineAttributeParser for AllowInternalUnstableParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::allow_internal_unstable];
|
||||
type Item = (Symbol, Span);
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable;
|
||||
|
||||
fn extend<'a>(
|
||||
cx: &'a AcceptContext<'a>,
|
||||
args: &'a ArgParser<'a>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'a {
|
||||
parse_unstable(cx, args, Self::PATH[0]).into_iter().zip(iter::repeat(cx.attr_span))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rustc_allow_const_fn_unstable(
|
||||
sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
) -> impl Iterator<Item = Symbol> {
|
||||
allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable)
|
||||
pub(crate) struct AllowConstFnUnstableParser;
|
||||
impl CombineAttributeParser for AllowConstFnUnstableParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_allow_const_fn_unstable];
|
||||
type Item = Symbol;
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable;
|
||||
|
||||
fn extend<'a>(
|
||||
cx: &'a AcceptContext<'a>,
|
||||
args: &'a ArgParser<'a>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'a {
|
||||
parse_unstable(cx, args, Self::PATH[0])
|
||||
}
|
||||
}
|
||||
|
||||
fn allow_unstable(
|
||||
sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
fn parse_unstable<'a>(
|
||||
cx: &AcceptContext<'_>,
|
||||
args: &'a ArgParser<'a>,
|
||||
symbol: Symbol,
|
||||
) -> impl Iterator<Item = Symbol> {
|
||||
let attrs = filter_by_name(attrs, symbol);
|
||||
let list = attrs
|
||||
.filter_map(move |attr| {
|
||||
attr.meta_item_list().or_else(|| {
|
||||
sess.dcx().emit_err(session_diagnostics::ExpectsFeatureList {
|
||||
span: attr.span(),
|
||||
) -> impl IntoIterator<Item = Symbol> {
|
||||
let mut res = Vec::new();
|
||||
|
||||
let Some(list) = args.list() else {
|
||||
cx.emit_err(session_diagnostics::ExpectsFeatureList {
|
||||
span: cx.attr_span,
|
||||
name: symbol.to_ident_string(),
|
||||
});
|
||||
None
|
||||
})
|
||||
})
|
||||
.flatten();
|
||||
return res;
|
||||
};
|
||||
|
||||
list.into_iter().filter_map(move |it| {
|
||||
let name = it.ident().map(|ident| ident.name);
|
||||
if name.is_none() {
|
||||
sess.dcx().emit_err(session_diagnostics::ExpectsFeatures {
|
||||
span: it.span(),
|
||||
for param in list.mixed() {
|
||||
let param_span = param.span();
|
||||
if let Some(ident) = param.meta_item().and_then(|i| i.word_without_args()) {
|
||||
res.push(ident.name);
|
||||
} else {
|
||||
cx.emit_err(session_diagnostics::ExpectsFeatures {
|
||||
span: param_span,
|
||||
name: symbol.to_ident_string(),
|
||||
});
|
||||
}
|
||||
name
|
||||
})
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
//! Parsing and validation of builtin attributes
|
||||
|
||||
use rustc_ast::{self as ast, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
|
||||
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr_data_structures::RustcVersion;
|
||||
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
|
||||
|
@ -9,10 +7,11 @@ use rustc_session::config::ExpectedValues;
|
|||
use rustc_session::lint::BuiltinLintDiag;
|
||||
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::{Span, Symbol, kw, sym};
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use crate::util::UnsupportedLiteralReason;
|
||||
use crate::{fluent_generated, parse_version, session_diagnostics};
|
||||
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
|
||||
use crate::{fluent_generated, parse_version};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Condition {
|
||||
|
@ -25,7 +24,7 @@ pub struct Condition {
|
|||
|
||||
/// Tests if a cfg-pattern matches the cfg set
|
||||
pub fn cfg_matches(
|
||||
cfg: &ast::MetaItemInner,
|
||||
cfg: &MetaItemInner,
|
||||
sess: &Session,
|
||||
lint_node_id: NodeId,
|
||||
features: Option<&Features>,
|
||||
|
@ -80,7 +79,7 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Fea
|
|||
/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
|
||||
/// evaluate individual items.
|
||||
pub fn eval_condition(
|
||||
cfg: &ast::MetaItemInner,
|
||||
cfg: &MetaItemInner,
|
||||
sess: &Session,
|
||||
features: Option<&Features>,
|
||||
eval: &mut impl FnMut(Condition) -> bool,
|
||||
|
@ -88,8 +87,8 @@ pub fn eval_condition(
|
|||
let dcx = sess.dcx();
|
||||
|
||||
let cfg = match cfg {
|
||||
ast::MetaItemInner::MetaItem(meta_item) => meta_item,
|
||||
ast::MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
|
||||
MetaItemInner::MetaItem(meta_item) => meta_item,
|
||||
MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
|
||||
if let Some(features) = features {
|
||||
// we can't use `try_gate_cfg` as symbols don't differentiate between `r#true`
|
||||
// and `true`, and we want to keep the former working without feature gate
|
||||
|
@ -118,7 +117,7 @@ pub fn eval_condition(
|
|||
};
|
||||
|
||||
match &cfg.kind {
|
||||
ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
|
||||
MetaItemKind::List(mis) if cfg.name_or_empty() == 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, .. })] => {
|
||||
|
@ -150,7 +149,7 @@ pub fn eval_condition(
|
|||
RustcVersion::CURRENT >= min_version
|
||||
}
|
||||
}
|
||||
ast::MetaItemKind::List(mis) => {
|
||||
MetaItemKind::List(mis) => {
|
||||
for mi in mis.iter() {
|
||||
if mi.meta_item_or_bool().is_none() {
|
||||
dcx.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
|
@ -209,12 +208,7 @@ pub fn eval_condition(
|
|||
seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
|
||||
}
|
||||
|
||||
res & eval_condition(
|
||||
&ast::MetaItemInner::MetaItem(mi),
|
||||
sess,
|
||||
features,
|
||||
eval,
|
||||
)
|
||||
res & eval_condition(&MetaItemInner::MetaItem(mi), sess, features, eval)
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
|
@ -226,7 +220,7 @@ pub fn eval_condition(
|
|||
}
|
||||
}
|
||||
}
|
||||
ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
|
||||
MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
|
||||
dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
|
||||
true
|
||||
}
|
||||
|
@ -239,7 +233,7 @@ pub fn eval_condition(
|
|||
});
|
||||
true
|
||||
}
|
||||
ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
|
||||
MetaItemKind::Word | MetaItemKind::NameValue(..) => {
|
||||
let ident = cfg.ident().expect("multi-segment cfg predicate");
|
||||
eval(Condition {
|
||||
name: ident.name,
|
||||
|
|
|
@ -1,21 +1,58 @@
|
|||
//! Parsing and validation of builtin attributes
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use rustc_ast::MetaItemInner;
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_span::Symbol;
|
||||
use super::{AcceptMapping, AttributeParser};
|
||||
use crate::context::FinalizeContext;
|
||||
use crate::session_diagnostics;
|
||||
|
||||
/// Read the content of a `rustc_confusables` attribute, and return the list of candidate names.
|
||||
pub fn parse_confusables(attr: &impl AttributeExt) -> Option<Vec<Symbol>> {
|
||||
let metas = attr.meta_item_list()?;
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ConfusablesParser {
|
||||
confusables: ThinVec<Symbol>,
|
||||
first_span: Option<Span>,
|
||||
}
|
||||
|
||||
let mut candidates = Vec::new();
|
||||
|
||||
for meta in metas {
|
||||
let MetaItemInner::Lit(meta_lit) = meta else {
|
||||
return None;
|
||||
impl AttributeParser for ConfusablesParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self> = &[(&[sym::rustc_confusables], |this, cx, args| {
|
||||
let Some(list) = args.list() else {
|
||||
// FIXME(jdonszelmann): error when not a list? Bring validation code here.
|
||||
// NOTE: currently subsequent attributes are silently ignored using
|
||||
// tcx.get_attr().
|
||||
return;
|
||||
};
|
||||
candidates.push(meta_lit.symbol);
|
||||
|
||||
if list.is_empty() {
|
||||
cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
|
||||
}
|
||||
|
||||
Some(candidates)
|
||||
for param in list.mixed() {
|
||||
let span = param.span();
|
||||
|
||||
let Some(lit) = param.lit() else {
|
||||
cx.emit_err(session_diagnostics::IncorrectMetaItem {
|
||||
span,
|
||||
suggestion: Some(session_diagnostics::IncorrectMetaItemSuggestion {
|
||||
lo: span.shrink_to_lo(),
|
||||
hi: span.shrink_to_hi(),
|
||||
}),
|
||||
});
|
||||
continue;
|
||||
};
|
||||
|
||||
this.confusables.push(lit.symbol);
|
||||
}
|
||||
|
||||
this.first_span.get_or_insert(cx.attr_span);
|
||||
})];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
if self.confusables.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(AttributeKind::Confusables {
|
||||
symbols: self.confusables,
|
||||
first_span: self.first_span.unwrap(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,121 +1,122 @@
|
|||
//! Parsing and validation of builtin attributes
|
||||
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_ast::{MetaItem, MetaItemInner};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr_data_structures::{DeprecatedSince, Deprecation};
|
||||
use rustc_feature::Features;
|
||||
use rustc_session::Session;
|
||||
use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::util::UnsupportedLiteralReason;
|
||||
use crate::{parse_version, session_diagnostics};
|
||||
use super::SingleAttributeParser;
|
||||
use super::util::parse_version;
|
||||
use crate::context::AcceptContext;
|
||||
use crate::parser::ArgParser;
|
||||
use crate::session_diagnostics;
|
||||
use crate::session_diagnostics::UnsupportedLiteralReason;
|
||||
|
||||
/// Finds the deprecation attribute. `None` if none exists.
|
||||
pub fn find_deprecation(
|
||||
sess: &Session,
|
||||
features: &Features,
|
||||
attrs: &[impl AttributeExt],
|
||||
) -> Option<(Deprecation, Span)> {
|
||||
let mut depr: Option<(Deprecation, Span)> = None;
|
||||
let is_rustc = features.staged_api();
|
||||
pub(crate) struct DeprecationParser;
|
||||
|
||||
'outer: for attr in attrs {
|
||||
if !attr.has_name(sym::deprecated) {
|
||||
continue;
|
||||
fn get(
|
||||
cx: &AcceptContext<'_>,
|
||||
ident: Ident,
|
||||
param_span: Span,
|
||||
arg: &ArgParser<'_>,
|
||||
item: &Option<Symbol>,
|
||||
) -> Option<Symbol> {
|
||||
if item.is_some() {
|
||||
cx.emit_err(session_diagnostics::MultipleItem {
|
||||
span: param_span,
|
||||
item: ident.to_string(),
|
||||
});
|
||||
return None;
|
||||
}
|
||||
if let Some(v) = arg.name_value() {
|
||||
if let Some(value_str) = v.value_as_str() {
|
||||
Some(value_str)
|
||||
} else {
|
||||
let lit = v.value_as_lit();
|
||||
cx.emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: v.value_span,
|
||||
reason: UnsupportedLiteralReason::DeprecatedString,
|
||||
is_bytestr: lit.kind.is_bytestr(),
|
||||
start_point_span: cx.sess().source_map().start_point(lit.span),
|
||||
});
|
||||
None
|
||||
}
|
||||
} else {
|
||||
// FIXME(jdonszelmann): suggestion?
|
||||
cx.emit_err(session_diagnostics::IncorrectMetaItem { span: param_span, suggestion: None });
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl SingleAttributeParser for DeprecationParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::deprecated];
|
||||
|
||||
fn on_duplicate(cx: &AcceptContext<'_>, first_span: rustc_span::Span) {
|
||||
// FIXME(jdonszelmann): merge with errors from check_attrs.rs
|
||||
cx.emit_err(session_diagnostics::UnusedMultiple {
|
||||
this: cx.attr_span,
|
||||
other: first_span,
|
||||
name: sym::deprecated,
|
||||
});
|
||||
}
|
||||
|
||||
fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
let features = cx.features();
|
||||
|
||||
let mut since = None;
|
||||
let mut note = None;
|
||||
let mut suggestion = None;
|
||||
|
||||
if attr.is_doc_comment() {
|
||||
continue;
|
||||
} else if attr.is_word() {
|
||||
} else if let Some(value) = attr.value_str() {
|
||||
note = Some(value)
|
||||
} else if let Some(list) = attr.meta_item_list() {
|
||||
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
|
||||
if item.is_some() {
|
||||
sess.dcx().emit_err(session_diagnostics::MultipleItem {
|
||||
span: meta.span,
|
||||
item: pprust::path_to_string(&meta.path),
|
||||
let is_rustc = features.staged_api();
|
||||
|
||||
if let Some(value) = args.name_value()
|
||||
&& let Some(value_str) = value.value_as_str()
|
||||
{
|
||||
note = Some(value_str)
|
||||
} else if let Some(list) = args.list() {
|
||||
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::DeprecatedKvPair,
|
||||
is_bytestr: false,
|
||||
start_point_span: cx.sess().source_map().start_point(param_span),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if let Some(v) = meta.value_str() {
|
||||
*item = Some(v);
|
||||
true
|
||||
} else {
|
||||
if let Some(lit) = meta.name_value_literal() {
|
||||
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: lit.span,
|
||||
reason: UnsupportedLiteralReason::DeprecatedString,
|
||||
is_bytestr: lit.kind.is_bytestr(),
|
||||
start_point_span: sess.source_map().start_point(lit.span),
|
||||
});
|
||||
} else {
|
||||
sess.dcx()
|
||||
.emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
|
||||
}
|
||||
false
|
||||
}
|
||||
return None;
|
||||
};
|
||||
|
||||
for meta in &list {
|
||||
match meta {
|
||||
MetaItemInner::MetaItem(mi) => match mi.name_or_empty() {
|
||||
let (ident, arg) = param.word_or_empty();
|
||||
|
||||
match ident.name {
|
||||
sym::since => {
|
||||
if !get(mi, &mut since) {
|
||||
continue 'outer;
|
||||
}
|
||||
since = Some(get(cx, ident, param_span, arg, &since)?);
|
||||
}
|
||||
sym::note => {
|
||||
if !get(mi, &mut note) {
|
||||
continue 'outer;
|
||||
}
|
||||
note = Some(get(cx, ident, param_span, arg, ¬e)?);
|
||||
}
|
||||
sym::suggestion => {
|
||||
if !features.deprecated_suggestion() {
|
||||
sess.dcx().emit_err(
|
||||
session_diagnostics::DeprecatedItemSuggestion {
|
||||
span: mi.span,
|
||||
is_nightly: sess.is_nightly_build(),
|
||||
cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
|
||||
span: param_span,
|
||||
is_nightly: cx.sess().is_nightly_build(),
|
||||
details: (),
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if !get(mi, &mut suggestion) {
|
||||
continue 'outer;
|
||||
}
|
||||
suggestion = Some(get(cx, ident, param_span, arg, &suggestion)?);
|
||||
}
|
||||
_ => {
|
||||
sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: meta.span(),
|
||||
item: pprust::path_to_string(&mi.path),
|
||||
cx.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: param_span,
|
||||
item: ident.to_string(),
|
||||
expected: if features.deprecated_suggestion() {
|
||||
&["since", "note", "suggestion"]
|
||||
} else {
|
||||
&["since", "note"]
|
||||
},
|
||||
});
|
||||
continue 'outer;
|
||||
}
|
||||
},
|
||||
MetaItemInner::Lit(lit) => {
|
||||
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: lit.span,
|
||||
reason: UnsupportedLiteralReason::DeprecatedKvPair,
|
||||
is_bytestr: false,
|
||||
start_point_span: sess.source_map().start_point(lit.span),
|
||||
});
|
||||
continue 'outer;
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
let since = if let Some(since) = since {
|
||||
|
@ -126,23 +127,24 @@ pub fn find_deprecation(
|
|||
} else if let Some(version) = parse_version(since) {
|
||||
DeprecatedSince::RustcVersion(version)
|
||||
} else {
|
||||
sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() });
|
||||
cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
|
||||
DeprecatedSince::Err
|
||||
}
|
||||
} else if is_rustc {
|
||||
sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span() });
|
||||
cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
|
||||
DeprecatedSince::Err
|
||||
} else {
|
||||
DeprecatedSince::Unspecified
|
||||
};
|
||||
|
||||
if is_rustc && note.is_none() {
|
||||
sess.dcx().emit_err(session_diagnostics::MissingNote { span: attr.span() });
|
||||
continue;
|
||||
cx.emit_err(session_diagnostics::MissingNote { span: cx.attr_span });
|
||||
return None;
|
||||
}
|
||||
|
||||
depr = Some((Deprecation { since, note, suggestion }, attr.span()));
|
||||
Some(AttributeKind::Deprecation {
|
||||
deprecation: Deprecation { since, note, suggestion },
|
||||
span: cx.attr_span,
|
||||
})
|
||||
}
|
||||
|
||||
depr
|
||||
}
|
||||
|
|
|
@ -32,14 +32,6 @@ pub(crate) mod stability;
|
|||
pub(crate) mod transparency;
|
||||
pub(crate) mod util;
|
||||
|
||||
pub use allow_unstable::*;
|
||||
pub use cfg::*;
|
||||
pub use confusables::*;
|
||||
pub use deprecation::*;
|
||||
pub use repr::*;
|
||||
pub use stability::*;
|
||||
pub use transparency::*;
|
||||
|
||||
type AcceptFn<T> = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>);
|
||||
type AcceptMapping<T> = &'static [(&'static [rustc_span::Symbol], AcceptFn<T>)];
|
||||
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
//! Parsing and validation of builtin attributes
|
||||
|
||||
use rustc_abi::Align;
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_ast::{self as ast, MetaItemKind};
|
||||
use rustc_attr_data_structures::IntType;
|
||||
use rustc_attr_data_structures::ReprAttr::*;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{Symbol, sym};
|
||||
use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
|
||||
use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use crate::ReprAttr;
|
||||
use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
|
||||
use super::{CombineAttributeParser, ConvertFn};
|
||||
use crate::context::AcceptContext;
|
||||
use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser};
|
||||
use crate::session_diagnostics;
|
||||
use crate::session_diagnostics::IncorrectReprFormatGenericCause;
|
||||
|
||||
/// Parse #[repr(...)] forms.
|
||||
///
|
||||
|
@ -18,185 +16,216 @@ use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
|
|||
/// the same discriminant size that the corresponding C enum would or C
|
||||
/// structure layout, `packed` to remove padding, and `transparent` to delegate representation
|
||||
/// concerns to the only non-ZST field.
|
||||
pub fn find_repr_attrs(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr> {
|
||||
if attr.has_name(sym::repr) { parse_repr_attr(sess, attr) } else { Vec::new() }
|
||||
// FIXME(jdonszelmann): is a vec the right representation here even? isn't it just a struct?
|
||||
pub(crate) struct ReprParser;
|
||||
|
||||
impl CombineAttributeParser for ReprParser {
|
||||
type Item = (ReprAttr, Span);
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::repr];
|
||||
const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
|
||||
|
||||
fn extend<'a>(
|
||||
cx: &'a AcceptContext<'a>,
|
||||
args: &'a ArgParser<'a>,
|
||||
) -> impl IntoIterator<Item = Self::Item> + 'a {
|
||||
let mut reprs = Vec::new();
|
||||
|
||||
let Some(list) = args.list() else {
|
||||
return reprs;
|
||||
};
|
||||
|
||||
if list.is_empty() {
|
||||
// this is so validation can emit a lint
|
||||
reprs.push((ReprAttr::ReprEmpty, cx.attr_span));
|
||||
}
|
||||
|
||||
for param in list.mixed() {
|
||||
if let Some(_) = param.lit() {
|
||||
cx.emit_err(session_diagnostics::ReprIdent { span: cx.attr_span });
|
||||
continue;
|
||||
}
|
||||
|
||||
reprs.extend(
|
||||
param.meta_item().and_then(|mi| parse_repr(cx, &mi)).map(|r| (r, param.span())),
|
||||
);
|
||||
}
|
||||
|
||||
reprs
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_repr_attr(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr> {
|
||||
assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}");
|
||||
let mut acc = Vec::new();
|
||||
let dcx = sess.dcx();
|
||||
|
||||
if let Some(items) = attr.meta_item_list() {
|
||||
for item in items {
|
||||
let mut recognised = false;
|
||||
if item.is_word() {
|
||||
let hint = match item.name_or_empty() {
|
||||
sym::Rust => Some(ReprRust),
|
||||
sym::C => Some(ReprC),
|
||||
sym::packed => Some(ReprPacked(Align::ONE)),
|
||||
sym::simd => Some(ReprSimd),
|
||||
sym::transparent => Some(ReprTransparent),
|
||||
sym::align => {
|
||||
sess.dcx().emit_err(session_diagnostics::InvalidReprAlignNeedArg {
|
||||
span: item.span(),
|
||||
});
|
||||
recognised = true;
|
||||
None
|
||||
}
|
||||
name => int_type_of_word(name).map(ReprInt),
|
||||
macro_rules! int_pat {
|
||||
() => {
|
||||
sym::i8
|
||||
| sym::u8
|
||||
| sym::i16
|
||||
| sym::u16
|
||||
| sym::i32
|
||||
| sym::u32
|
||||
| sym::i64
|
||||
| sym::u64
|
||||
| sym::i128
|
||||
| sym::u128
|
||||
| sym::isize
|
||||
| sym::usize
|
||||
};
|
||||
|
||||
if let Some(h) = hint {
|
||||
recognised = true;
|
||||
acc.push(h);
|
||||
}
|
||||
} else if let Some((name, value)) = item.singleton_lit_list() {
|
||||
let mut literal_error = None;
|
||||
let mut err_span = item.span();
|
||||
if name == sym::align {
|
||||
recognised = true;
|
||||
match parse_alignment(&value.kind) {
|
||||
Ok(literal) => acc.push(ReprAlign(literal)),
|
||||
Err(message) => {
|
||||
err_span = value.span;
|
||||
literal_error = Some(message)
|
||||
}
|
||||
};
|
||||
} else if name == sym::packed {
|
||||
recognised = true;
|
||||
match parse_alignment(&value.kind) {
|
||||
Ok(literal) => acc.push(ReprPacked(literal)),
|
||||
Err(message) => {
|
||||
err_span = value.span;
|
||||
literal_error = Some(message)
|
||||
}
|
||||
};
|
||||
} else if matches!(name, sym::Rust | sym::C | sym::simd | sym::transparent)
|
||||
|| int_type_of_word(name).is_some()
|
||||
{
|
||||
recognised = true;
|
||||
sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen {
|
||||
span: item.span(),
|
||||
name: name.to_ident_string(),
|
||||
});
|
||||
}
|
||||
if let Some(literal_error) = literal_error {
|
||||
sess.dcx().emit_err(session_diagnostics::InvalidReprGeneric {
|
||||
span: err_span,
|
||||
repr_arg: name.to_ident_string(),
|
||||
error_part: literal_error,
|
||||
});
|
||||
}
|
||||
} else if let Some(meta_item) = item.meta_item() {
|
||||
match &meta_item.kind {
|
||||
MetaItemKind::NameValue(value) => {
|
||||
if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) {
|
||||
let name = meta_item.name_or_empty().to_ident_string();
|
||||
recognised = true;
|
||||
sess.dcx().emit_err(session_diagnostics::IncorrectReprFormatGeneric {
|
||||
span: item.span(),
|
||||
repr_arg: &name,
|
||||
cause: IncorrectReprFormatGenericCause::from_lit_kind(
|
||||
item.span(),
|
||||
&value.kind,
|
||||
&name,
|
||||
),
|
||||
});
|
||||
} else if matches!(
|
||||
meta_item.name_or_empty(),
|
||||
sym::Rust | sym::C | sym::simd | sym::transparent
|
||||
) || int_type_of_word(meta_item.name_or_empty()).is_some()
|
||||
{
|
||||
recognised = true;
|
||||
sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoValue {
|
||||
span: meta_item.span,
|
||||
name: meta_item.name_or_empty().to_ident_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
MetaItemKind::List(nested_items) => {
|
||||
if meta_item.has_name(sym::align) {
|
||||
recognised = true;
|
||||
if let [nested_item] = nested_items.as_slice() {
|
||||
sess.dcx().emit_err(
|
||||
session_diagnostics::IncorrectReprFormatExpectInteger {
|
||||
span: nested_item.span(),
|
||||
},
|
||||
);
|
||||
} else {
|
||||
sess.dcx().emit_err(
|
||||
session_diagnostics::IncorrectReprFormatAlignOneArg {
|
||||
span: meta_item.span,
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if meta_item.has_name(sym::packed) {
|
||||
recognised = true;
|
||||
if let [nested_item] = nested_items.as_slice() {
|
||||
sess.dcx().emit_err(
|
||||
session_diagnostics::IncorrectReprFormatPackedExpectInteger {
|
||||
span: nested_item.span(),
|
||||
},
|
||||
);
|
||||
} else {
|
||||
sess.dcx().emit_err(
|
||||
session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
|
||||
span: meta_item.span,
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if matches!(
|
||||
meta_item.name_or_empty(),
|
||||
sym::Rust | sym::C | sym::simd | sym::transparent
|
||||
) || int_type_of_word(meta_item.name_or_empty()).is_some()
|
||||
{
|
||||
recognised = true;
|
||||
sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen {
|
||||
span: meta_item.span,
|
||||
name: meta_item.name_or_empty().to_ident_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
if !recognised {
|
||||
// Not a word we recognize. This will be caught and reported by
|
||||
// the `check_mod_attrs` pass, but this pass doesn't always run
|
||||
// (e.g. if we only pretty-print the source), so we have to gate
|
||||
// the `span_delayed_bug` call as follows:
|
||||
if sess.opts.pretty.is_none_or(|pp| pp.needs_analysis()) {
|
||||
dcx.span_delayed_bug(item.span(), "unrecognized representation hint");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
acc
|
||||
}
|
||||
|
||||
fn int_type_of_word(s: Symbol) -> Option<IntType> {
|
||||
use rustc_attr_data_structures::IntType::*;
|
||||
use IntType::*;
|
||||
|
||||
match s {
|
||||
sym::i8 => Some(SignedInt(ast::IntTy::I8)),
|
||||
sym::u8 => Some(UnsignedInt(ast::UintTy::U8)),
|
||||
sym::i16 => Some(SignedInt(ast::IntTy::I16)),
|
||||
sym::u16 => Some(UnsignedInt(ast::UintTy::U16)),
|
||||
sym::i32 => Some(SignedInt(ast::IntTy::I32)),
|
||||
sym::u32 => Some(UnsignedInt(ast::UintTy::U32)),
|
||||
sym::i64 => Some(SignedInt(ast::IntTy::I64)),
|
||||
sym::u64 => Some(UnsignedInt(ast::UintTy::U64)),
|
||||
sym::i128 => Some(SignedInt(ast::IntTy::I128)),
|
||||
sym::u128 => Some(UnsignedInt(ast::UintTy::U128)),
|
||||
sym::isize => Some(SignedInt(ast::IntTy::Isize)),
|
||||
sym::usize => Some(UnsignedInt(ast::UintTy::Usize)),
|
||||
sym::i8 => Some(SignedInt(IntTy::I8)),
|
||||
sym::u8 => Some(UnsignedInt(UintTy::U8)),
|
||||
sym::i16 => Some(SignedInt(IntTy::I16)),
|
||||
sym::u16 => Some(UnsignedInt(UintTy::U16)),
|
||||
sym::i32 => Some(SignedInt(IntTy::I32)),
|
||||
sym::u32 => Some(UnsignedInt(UintTy::U32)),
|
||||
sym::i64 => Some(SignedInt(IntTy::I64)),
|
||||
sym::u64 => Some(UnsignedInt(UintTy::U64)),
|
||||
sym::i128 => Some(SignedInt(IntTy::I128)),
|
||||
sym::u128 => Some(UnsignedInt(UintTy::U128)),
|
||||
sym::isize => Some(SignedInt(IntTy::Isize)),
|
||||
sym::usize => Some(UnsignedInt(UintTy::Usize)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_alignment(node: &ast::LitKind) -> Result<Align, &'static str> {
|
||||
if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
|
||||
fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option<ReprAttr> {
|
||||
use ReprAttr::*;
|
||||
|
||||
// FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
|
||||
// structure.
|
||||
let (ident, args) = param.word_or_empty();
|
||||
|
||||
match (ident.name, args) {
|
||||
(sym::align, ArgParser::NoArgs) => {
|
||||
cx.emit_err(session_diagnostics::InvalidReprAlignNeedArg { span: ident.span });
|
||||
None
|
||||
}
|
||||
(sym::align, ArgParser::List(l)) => parse_repr_align(cx, l, param.span(), AlignKind::Align),
|
||||
|
||||
(sym::packed, ArgParser::NoArgs) => Some(ReprPacked(Align::ONE)),
|
||||
(sym::packed, ArgParser::List(l)) => {
|
||||
parse_repr_align(cx, l, param.span(), AlignKind::Packed)
|
||||
}
|
||||
|
||||
(sym::align | sym::packed, ArgParser::NameValue(l)) => {
|
||||
cx.emit_err(session_diagnostics::IncorrectReprFormatGeneric {
|
||||
span: param.span(),
|
||||
// FIXME(jdonszelmann) can just be a string in the diag type
|
||||
repr_arg: &ident.to_string(),
|
||||
cause: IncorrectReprFormatGenericCause::from_lit_kind(
|
||||
param.span(),
|
||||
&l.value_as_lit().kind,
|
||||
ident.name.as_str(),
|
||||
),
|
||||
});
|
||||
None
|
||||
}
|
||||
|
||||
(sym::Rust, ArgParser::NoArgs) => Some(ReprRust),
|
||||
(sym::C, ArgParser::NoArgs) => Some(ReprC),
|
||||
(sym::simd, ArgParser::NoArgs) => Some(ReprSimd),
|
||||
(sym::transparent, ArgParser::NoArgs) => Some(ReprTransparent),
|
||||
(i @ int_pat!(), ArgParser::NoArgs) => {
|
||||
// int_pat!() should make sure it always parses
|
||||
Some(ReprInt(int_type_of_word(i).unwrap()))
|
||||
}
|
||||
|
||||
(
|
||||
sym::Rust | sym::C | sym::simd | sym::transparent | int_pat!(),
|
||||
ArgParser::NameValue(_),
|
||||
) => {
|
||||
cx.emit_err(session_diagnostics::InvalidReprHintNoValue {
|
||||
span: param.span(),
|
||||
name: ident.to_string(),
|
||||
});
|
||||
None
|
||||
}
|
||||
(sym::Rust | sym::C | sym::simd | sym::transparent | int_pat!(), ArgParser::List(_)) => {
|
||||
cx.emit_err(session_diagnostics::InvalidReprHintNoParen {
|
||||
span: param.span(),
|
||||
name: ident.to_string(),
|
||||
});
|
||||
None
|
||||
}
|
||||
|
||||
_ => {
|
||||
cx.emit_err(session_diagnostics::UnrecognizedReprHint { span: param.span() });
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum AlignKind {
|
||||
Packed,
|
||||
Align,
|
||||
}
|
||||
|
||||
fn parse_repr_align(
|
||||
cx: &AcceptContext<'_>,
|
||||
list: &MetaItemListParser<'_>,
|
||||
param_span: Span,
|
||||
align_kind: AlignKind,
|
||||
) -> Option<ReprAttr> {
|
||||
use AlignKind::*;
|
||||
|
||||
let Some(align) = list.single() else {
|
||||
match align_kind {
|
||||
Packed => {
|
||||
cx.emit_err(session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
|
||||
span: param_span,
|
||||
});
|
||||
}
|
||||
Align => {
|
||||
cx.dcx().emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
|
||||
span: param_span,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(lit) = align.lit() else {
|
||||
match align_kind {
|
||||
Packed => {
|
||||
cx.emit_err(session_diagnostics::IncorrectReprFormatPackedExpectInteger {
|
||||
span: align.span(),
|
||||
});
|
||||
}
|
||||
Align => {
|
||||
cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
|
||||
span: align.span(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return None;
|
||||
};
|
||||
|
||||
match parse_alignment(&lit.kind) {
|
||||
Ok(literal) => Some(match align_kind {
|
||||
AlignKind::Packed => ReprAttr::ReprPacked(literal),
|
||||
AlignKind::Align => ReprAttr::ReprAlign(literal),
|
||||
}),
|
||||
Err(message) => {
|
||||
cx.emit_err(session_diagnostics::InvalidReprGeneric {
|
||||
span: lit.span,
|
||||
repr_arg: match align_kind {
|
||||
Packed => "packed".to_string(),
|
||||
Align => "align".to_string(),
|
||||
},
|
||||
error_part: message,
|
||||
});
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
|
||||
if let LitKind::Int(literal, LitIntType::Unsuffixed) = node {
|
||||
// `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first
|
||||
if literal.get().is_power_of_two() {
|
||||
// Only possible error is larger than 2^29
|
||||
|
|
|
@ -1,266 +1,258 @@
|
|||
//! Parsing and validation of builtin attributes
|
||||
|
||||
use std::num::NonZero;
|
||||
|
||||
use rustc_ast::MetaItem;
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr_data_structures::{
|
||||
ConstStability, DefaultBodyStability, Stability, StabilityLevel, StableSince, UnstableReason,
|
||||
VERSION_PLACEHOLDER,
|
||||
AttributeKind, DefaultBodyStability, PartialConstStability, Stability, StabilityLevel,
|
||||
StableSince, UnstableReason, VERSION_PLACEHOLDER,
|
||||
};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{Span, Symbol, kw, sym};
|
||||
|
||||
use crate::attributes::util::UnsupportedLiteralReason;
|
||||
use crate::{parse_version, session_diagnostics};
|
||||
use super::util::parse_version;
|
||||
use super::{AcceptMapping, AttributeParser, SingleAttributeParser};
|
||||
use crate::context::{AcceptContext, FinalizeContext};
|
||||
use crate::parser::{ArgParser, MetaItemParser};
|
||||
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
|
||||
|
||||
/// Collects stability info from `stable`/`unstable`/`rustc_allowed_through_unstable_modules`
|
||||
/// attributes in `attrs`. Returns `None` if no stability attributes are found.
|
||||
pub fn find_stability(
|
||||
sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
item_sp: Span,
|
||||
) -> Option<(Stability, Span)> {
|
||||
let mut stab: Option<(Stability, Span)> = None;
|
||||
let mut allowed_through_unstable_modules = None;
|
||||
|
||||
for attr in attrs {
|
||||
match attr.name_or_empty() {
|
||||
sym::rustc_allowed_through_unstable_modules => {
|
||||
// The value is mandatory, but avoid ICEs in case such code reaches this function.
|
||||
allowed_through_unstable_modules = Some(attr.value_str().unwrap_or_else(|| {
|
||||
sess.dcx().span_delayed_bug(
|
||||
item_sp,
|
||||
"`#[rustc_allowed_through_unstable_modules]` without deprecation message",
|
||||
);
|
||||
kw::Empty
|
||||
}))
|
||||
macro_rules! reject_outside_std {
|
||||
($cx: ident) => {
|
||||
// Emit errors for non-staged-api crates.
|
||||
if !$cx.features().staged_api() {
|
||||
$cx.emit_err(session_diagnostics::StabilityOutsideStd { span: $cx.attr_span });
|
||||
return;
|
||||
}
|
||||
sym::unstable => {
|
||||
if stab.is_some() {
|
||||
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
|
||||
span: attr.span(),
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct StabilityParser {
|
||||
allowed_through_unstable_modules: Option<Symbol>,
|
||||
stability: Option<(Stability, Span)>,
|
||||
}
|
||||
|
||||
impl StabilityParser {
|
||||
/// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate.
|
||||
fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool {
|
||||
if let Some((_, _)) = self.stability {
|
||||
cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributeParser for StabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self> = &[
|
||||
(&[sym::stable], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_stability(cx, args)
|
||||
{
|
||||
this.stability = Some((Stability { level, feature }, cx.attr_span));
|
||||
}
|
||||
}),
|
||||
(&[sym::unstable], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_unstability(cx, args)
|
||||
{
|
||||
this.stability = Some((Stability { level, feature }, cx.attr_span));
|
||||
}
|
||||
}),
|
||||
(&[sym::rustc_allowed_through_unstable_modules], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
this.allowed_through_unstable_modules =
|
||||
Some(match args.name_value().and_then(|i| i.value_as_str()) {
|
||||
Some(msg) => msg,
|
||||
None => kw::Empty,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}),
|
||||
];
|
||||
|
||||
if let Some((feature, level)) = parse_unstability(sess, attr) {
|
||||
stab = Some((Stability { level, feature }, attr.span()));
|
||||
}
|
||||
}
|
||||
sym::stable => {
|
||||
if stab.is_some() {
|
||||
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
|
||||
span: attr.span(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
if let Some((feature, level)) = parse_stability(sess, attr) {
|
||||
stab = Some((Stability { level, feature }, attr.span()));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(allowed_through_unstable_modules) = allowed_through_unstable_modules {
|
||||
match &mut stab {
|
||||
Some((
|
||||
fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
if let Some(atum) = self.allowed_through_unstable_modules {
|
||||
if let Some((
|
||||
Stability {
|
||||
level: StabilityLevel::Stable { allowed_through_unstable_modules: in_stab, .. },
|
||||
level: StabilityLevel::Stable { ref mut allowed_through_unstable_modules, .. },
|
||||
..
|
||||
},
|
||||
_,
|
||||
)) => *in_stab = Some(allowed_through_unstable_modules),
|
||||
_ => {
|
||||
sess.dcx()
|
||||
.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stab
|
||||
}
|
||||
|
||||
/// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable`
|
||||
/// attributes in `attrs`. Returns `None` if no stability attributes are found.
|
||||
pub fn find_const_stability(
|
||||
sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
item_sp: Span,
|
||||
) -> Option<(ConstStability, Span)> {
|
||||
let mut const_stab: Option<(ConstStability, Span)> = None;
|
||||
let mut promotable = false;
|
||||
let mut const_stable_indirect = false;
|
||||
|
||||
for attr in attrs {
|
||||
match attr.name_or_empty() {
|
||||
sym::rustc_promotable => promotable = true,
|
||||
sym::rustc_const_stable_indirect => const_stable_indirect = true,
|
||||
sym::rustc_const_unstable => {
|
||||
if const_stab.is_some() {
|
||||
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
|
||||
span: attr.span(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some((feature, level)) = parse_unstability(sess, attr) {
|
||||
const_stab = Some((
|
||||
ConstStability {
|
||||
level,
|
||||
feature,
|
||||
const_stable_indirect: false,
|
||||
promotable: false,
|
||||
},
|
||||
attr.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
sym::rustc_const_stable => {
|
||||
if const_stab.is_some() {
|
||||
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
|
||||
span: attr.span(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
if let Some((feature, level)) = parse_stability(sess, attr) {
|
||||
const_stab = Some((
|
||||
ConstStability {
|
||||
level,
|
||||
feature,
|
||||
const_stable_indirect: false,
|
||||
promotable: false,
|
||||
},
|
||||
attr.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge promotable and const_stable_indirect into stability info
|
||||
if promotable {
|
||||
match &mut const_stab {
|
||||
Some((stab, _)) => stab.promotable = promotable,
|
||||
_ => {
|
||||
_ = sess
|
||||
.dcx()
|
||||
.emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp })
|
||||
}
|
||||
}
|
||||
}
|
||||
if const_stable_indirect {
|
||||
match &mut const_stab {
|
||||
Some((stab, _)) => {
|
||||
if stab.is_const_unstable() {
|
||||
stab.const_stable_indirect = true;
|
||||
)) = self.stability
|
||||
{
|
||||
*allowed_through_unstable_modules = Some(atum);
|
||||
} else {
|
||||
_ = sess.dcx().emit_err(session_diagnostics::RustcConstStableIndirectPairing {
|
||||
span: item_sp,
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// This function has no const stability attribute, but has `const_stable_indirect`.
|
||||
// We ignore that; unmarked functions are subject to recursive const stability
|
||||
// checks by default so we do carry out the user's intent.
|
||||
}
|
||||
cx.dcx().emit_err(session_diagnostics::RustcAllowedUnstablePairing {
|
||||
span: cx.target_span,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const_stab
|
||||
}
|
||||
let (stability, span) = self.stability?;
|
||||
|
||||
/// Calculates the const stability for a const function in a `-Zforce-unstable-if-unmarked` crate
|
||||
/// without the `staged_api` feature.
|
||||
pub fn unmarked_crate_const_stab(
|
||||
_sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
regular_stab: Stability,
|
||||
) -> ConstStability {
|
||||
assert!(regular_stab.level.is_unstable());
|
||||
// The only attribute that matters here is `rustc_const_stable_indirect`.
|
||||
// We enforce recursive const stability rules for those functions.
|
||||
let const_stable_indirect =
|
||||
attrs.iter().any(|a| a.name_or_empty() == sym::rustc_const_stable_indirect);
|
||||
ConstStability {
|
||||
feature: regular_stab.feature,
|
||||
const_stable_indirect,
|
||||
promotable: false,
|
||||
level: regular_stab.level,
|
||||
Some(AttributeKind::Stability { stability, span })
|
||||
}
|
||||
}
|
||||
|
||||
/// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`.
|
||||
/// Returns `None` if no stability attributes are found.
|
||||
pub fn find_body_stability(
|
||||
sess: &Session,
|
||||
attrs: &[impl AttributeExt],
|
||||
) -> Option<(DefaultBodyStability, Span)> {
|
||||
let mut body_stab: Option<(DefaultBodyStability, Span)> = None;
|
||||
|
||||
for attr in attrs {
|
||||
if attr.has_name(sym::rustc_default_body_unstable) {
|
||||
if body_stab.is_some() {
|
||||
sess.dcx()
|
||||
.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span() });
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some((feature, level)) = parse_unstability(sess, attr) {
|
||||
body_stab = Some((DefaultBodyStability { level, feature }, attr.span()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body_stab
|
||||
// FIXME(jdonszelmann) change to Single
|
||||
#[derive(Default)]
|
||||
pub(crate) struct BodyStabilityParser {
|
||||
stability: Option<(DefaultBodyStability, Span)>,
|
||||
}
|
||||
|
||||
fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -> Option<()> {
|
||||
impl AttributeParser for BodyStabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self> =
|
||||
&[(&[sym::rustc_default_body_unstable], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if this.stability.is_some() {
|
||||
cx.dcx()
|
||||
.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
|
||||
} else if let Some((feature, level)) = parse_unstability(cx, args) {
|
||||
this.stability = Some((DefaultBodyStability { level, feature }, cx.attr_span));
|
||||
}
|
||||
})];
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
let (stability, span) = self.stability?;
|
||||
|
||||
Some(AttributeKind::BodyStability { stability, span })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ConstStabilityIndirectParser;
|
||||
// FIXME(jdonszelmann): single word attribute group when we have these
|
||||
impl SingleAttributeParser for ConstStabilityIndirectParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_const_stable_indirect];
|
||||
|
||||
// ignore
|
||||
fn on_duplicate(_cx: &AcceptContext<'_>, _first_span: Span) {}
|
||||
|
||||
fn convert(_cx: &AcceptContext<'_>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
Some(AttributeKind::ConstStabilityIndirect)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ConstStabilityParser {
|
||||
promotable: bool,
|
||||
stability: Option<(PartialConstStability, Span)>,
|
||||
}
|
||||
|
||||
impl ConstStabilityParser {
|
||||
/// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate.
|
||||
fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool {
|
||||
if let Some((_, _)) = self.stability {
|
||||
cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributeParser for ConstStabilityParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self> = &[
|
||||
(&[sym::rustc_const_stable], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_stability(cx, args)
|
||||
{
|
||||
this.stability = Some((
|
||||
PartialConstStability { level, feature, promotable: false },
|
||||
cx.attr_span,
|
||||
));
|
||||
}
|
||||
}),
|
||||
(&[sym::rustc_const_unstable], |this, cx, args| {
|
||||
reject_outside_std!(cx);
|
||||
if !this.check_duplicate(cx)
|
||||
&& let Some((feature, level)) = parse_unstability(cx, args)
|
||||
{
|
||||
this.stability = Some((
|
||||
PartialConstStability { level, feature, promotable: false },
|
||||
cx.attr_span,
|
||||
));
|
||||
}
|
||||
}),
|
||||
(&[sym::rustc_promotable], |this, cx, _| {
|
||||
reject_outside_std!(cx);
|
||||
this.promotable = true;
|
||||
}),
|
||||
];
|
||||
|
||||
fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
|
||||
if self.promotable {
|
||||
if let Some((ref mut stab, _)) = self.stability {
|
||||
stab.promotable = true;
|
||||
} else {
|
||||
cx.dcx()
|
||||
.emit_err(session_diagnostics::RustcPromotablePairing { span: cx.target_span });
|
||||
}
|
||||
}
|
||||
|
||||
let (stability, span) = self.stability?;
|
||||
|
||||
Some(AttributeKind::ConstStability { stability, span })
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to insert the value of a `key = value` meta item into an option.
|
||||
///
|
||||
/// Emits an error when either the option was already Some, or the arguments weren't of form
|
||||
/// `name = value`
|
||||
fn insert_value_into_option_or_error(
|
||||
cx: &AcceptContext<'_>,
|
||||
param: &MetaItemParser<'_>,
|
||||
item: &mut Option<Symbol>,
|
||||
) -> Option<()> {
|
||||
if item.is_some() {
|
||||
sess.dcx().emit_err(session_diagnostics::MultipleItem {
|
||||
span: meta.span,
|
||||
item: pprust::path_to_string(&meta.path),
|
||||
cx.emit_err(session_diagnostics::MultipleItem {
|
||||
span: param.span(),
|
||||
item: param.path_without_args().to_string(),
|
||||
});
|
||||
None
|
||||
} else if let Some(v) = meta.value_str() {
|
||||
*item = Some(v);
|
||||
} else if let Some(v) = param.args().name_value()
|
||||
&& let Some(s) = v.value_as_str()
|
||||
{
|
||||
*item = Some(s);
|
||||
Some(())
|
||||
} else {
|
||||
sess.dcx().emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
|
||||
cx.emit_err(session_diagnostics::IncorrectMetaItem {
|
||||
span: param.span(),
|
||||
suggestion: None,
|
||||
});
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and
|
||||
/// its stability information.
|
||||
fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> {
|
||||
let metas = attr.meta_item_list()?;
|
||||
|
||||
pub(crate) fn parse_stability(
|
||||
cx: &AcceptContext<'_>,
|
||||
args: &ArgParser<'_>,
|
||||
) -> Option<(Symbol, StabilityLevel)> {
|
||||
let mut feature = None;
|
||||
let mut since = None;
|
||||
for meta in metas {
|
||||
let Some(mi) = meta.meta_item() else {
|
||||
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: meta.span(),
|
||||
|
||||
for param in args.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: sess.source_map().start_point(meta.span()),
|
||||
start_point_span: cx.sess().source_map().start_point(param_span),
|
||||
});
|
||||
return None;
|
||||
};
|
||||
|
||||
match mi.name_or_empty() {
|
||||
sym::feature => insert_or_error(sess, mi, &mut feature)?,
|
||||
sym::since => insert_or_error(sess, mi, &mut since)?,
|
||||
match param.word_or_empty_without_args().name {
|
||||
sym::feature => insert_value_into_option_or_error(cx, ¶m, &mut feature)?,
|
||||
sym::since => insert_value_into_option_or_error(cx, ¶m, &mut since)?,
|
||||
_ => {
|
||||
sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: meta.span(),
|
||||
item: pprust::path_to_string(&mi.path),
|
||||
cx.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: param_span,
|
||||
item: param.path_without_args().to_string(),
|
||||
expected: &["feature", "since"],
|
||||
});
|
||||
return None;
|
||||
|
@ -271,9 +263,9 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
|
|||
let feature = match feature {
|
||||
Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
|
||||
Some(_bad_feature) => {
|
||||
Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span() }))
|
||||
Err(cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span }))
|
||||
}
|
||||
None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span() })),
|
||||
None => Err(cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })),
|
||||
};
|
||||
|
||||
let since = if let Some(since) = since {
|
||||
|
@ -282,11 +274,11 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
|
|||
} else if let Some(version) = parse_version(since) {
|
||||
StableSince::Version(version)
|
||||
} else {
|
||||
sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() });
|
||||
cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
|
||||
StableSince::Err
|
||||
}
|
||||
} else {
|
||||
sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span() });
|
||||
cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
|
||||
StableSince::Err
|
||||
};
|
||||
|
||||
|
@ -299,46 +291,48 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
|
|||
}
|
||||
}
|
||||
|
||||
/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
|
||||
// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
|
||||
/// attribute, and return the feature name and its stability information.
|
||||
fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> {
|
||||
let metas = attr.meta_item_list()?;
|
||||
|
||||
pub(crate) fn parse_unstability(
|
||||
cx: &AcceptContext<'_>,
|
||||
args: &ArgParser<'_>,
|
||||
) -> Option<(Symbol, StabilityLevel)> {
|
||||
let mut feature = None;
|
||||
let mut reason = None;
|
||||
let mut issue = None;
|
||||
let mut issue_num = None;
|
||||
let mut is_soft = false;
|
||||
let mut implied_by = None;
|
||||
for meta in metas {
|
||||
let Some(mi) = meta.meta_item() else {
|
||||
sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
|
||||
span: meta.span(),
|
||||
for param in args.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: sess.source_map().start_point(meta.span()),
|
||||
start_point_span: cx.sess().source_map().start_point(param.span()),
|
||||
});
|
||||
return None;
|
||||
};
|
||||
|
||||
match mi.name_or_empty() {
|
||||
sym::feature => insert_or_error(sess, mi, &mut feature)?,
|
||||
sym::reason => insert_or_error(sess, mi, &mut reason)?,
|
||||
let (word, args) = param.word_or_empty();
|
||||
match word.name {
|
||||
sym::feature => insert_value_into_option_or_error(cx, ¶m, &mut feature)?,
|
||||
sym::reason => insert_value_into_option_or_error(cx, ¶m, &mut reason)?,
|
||||
sym::issue => {
|
||||
insert_or_error(sess, mi, &mut issue)?;
|
||||
insert_value_into_option_or_error(cx, ¶m, &mut issue)?;
|
||||
|
||||
// These unwraps are safe because `insert_or_error` ensures the meta item
|
||||
// These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item
|
||||
// is a name/value pair string literal.
|
||||
issue_num = match issue.unwrap().as_str() {
|
||||
"none" => None,
|
||||
issue => match issue.parse::<NonZero<u32>>() {
|
||||
issue_str => match issue_str.parse::<NonZero<u32>>() {
|
||||
Ok(num) => Some(num),
|
||||
Err(err) => {
|
||||
sess.dcx().emit_err(
|
||||
cx.emit_err(
|
||||
session_diagnostics::InvalidIssueString {
|
||||
span: mi.span,
|
||||
span: param.span(),
|
||||
cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind(
|
||||
mi.name_value_literal_span().unwrap(),
|
||||
args.name_value().unwrap().value_span,
|
||||
err.kind(),
|
||||
),
|
||||
},
|
||||
|
@ -349,16 +343,16 @@ fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol
|
|||
};
|
||||
}
|
||||
sym::soft => {
|
||||
if !mi.is_word() {
|
||||
sess.dcx().emit_err(session_diagnostics::SoftNoArgs { span: mi.span });
|
||||
if !args.no_args() {
|
||||
cx.emit_err(session_diagnostics::SoftNoArgs { span: param.span() });
|
||||
}
|
||||
is_soft = true;
|
||||
}
|
||||
sym::implied_by => insert_or_error(sess, mi, &mut implied_by)?,
|
||||
sym::implied_by => insert_value_into_option_or_error(cx, ¶m, &mut implied_by)?,
|
||||
_ => {
|
||||
sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: meta.span(),
|
||||
item: pprust::path_to_string(&mi.path),
|
||||
cx.emit_err(session_diagnostics::UnknownMetaItem {
|
||||
span: param.span(),
|
||||
item: param.path_without_args().to_string(),
|
||||
expected: &["feature", "reason", "issue", "soft", "implied_by"],
|
||||
});
|
||||
return None;
|
||||
|
@ -369,14 +363,13 @@ fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol
|
|||
let feature = match feature {
|
||||
Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
|
||||
Some(_bad_feature) => {
|
||||
Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span() }))
|
||||
Err(cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span }))
|
||||
}
|
||||
None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span() })),
|
||||
None => Err(cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })),
|
||||
};
|
||||
|
||||
let issue = issue.ok_or_else(|| {
|
||||
sess.dcx().emit_err(session_diagnostics::MissingIssue { span: attr.span() })
|
||||
});
|
||||
let issue =
|
||||
issue.ok_or_else(|| cx.emit_err(session_diagnostics::MissingIssue { span: cx.attr_span }));
|
||||
|
||||
match (feature, issue) {
|
||||
(Ok(feature), Ok(_)) => {
|
||||
|
|
|
@ -1,36 +1,33 @@
|
|||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_attr_data_structures::TransparencyError;
|
||||
use rustc_attr_data_structures::AttributeKind;
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::sym;
|
||||
|
||||
pub fn find_transparency(
|
||||
attrs: &[impl AttributeExt],
|
||||
macro_rules: bool,
|
||||
) -> (Transparency, Option<TransparencyError>) {
|
||||
let mut transparency = None;
|
||||
let mut error = None;
|
||||
for attr in attrs {
|
||||
if attr.has_name(sym::rustc_macro_transparency) {
|
||||
if let Some((_, old_span)) = transparency {
|
||||
error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span()));
|
||||
break;
|
||||
} else if let Some(value) = attr.value_str() {
|
||||
transparency = Some((
|
||||
match value {
|
||||
sym::transparent => Transparency::Transparent,
|
||||
sym::semitransparent => Transparency::SemiTransparent,
|
||||
sym::opaque => Transparency::Opaque,
|
||||
_ => {
|
||||
error =
|
||||
Some(TransparencyError::UnknownTransparency(value, attr.span()));
|
||||
continue;
|
||||
use super::{AcceptContext, SingleAttributeParser};
|
||||
use crate::parser::ArgParser;
|
||||
|
||||
pub(crate) struct TransparencyParser;
|
||||
|
||||
// FIXME(jdonszelmann): make these proper diagnostics
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
impl SingleAttributeParser for TransparencyParser {
|
||||
const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_macro_transparency];
|
||||
|
||||
fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: rustc_span::Span) {
|
||||
cx.dcx().span_err(vec![first_span, cx.attr_span], "multiple macro transparency attributes");
|
||||
}
|
||||
},
|
||||
attr.span(),
|
||||
));
|
||||
|
||||
fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
|
||||
match args.name_value().and_then(|nv| nv.value_as_str()) {
|
||||
Some(sym::transparent) => Some(Transparency::Transparent),
|
||||
Some(sym::semitransparent) => Some(Transparency::SemiTransparent),
|
||||
Some(sym::opaque) => Some(Transparency::Opaque),
|
||||
Some(other) => {
|
||||
cx.dcx().span_err(cx.attr_span, format!("unknown macro transparency: `{other}`"));
|
||||
None
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
.map(AttributeKind::MacroTransparency)
|
||||
}
|
||||
let fallback = if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque };
|
||||
(transparency.map_or(fallback, |t| t.0), error)
|
||||
}
|
||||
|
|
|
@ -3,22 +3,6 @@ use rustc_attr_data_structures::RustcVersion;
|
|||
use rustc_feature::is_builtin_attr_name;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
pub(crate) enum UnsupportedLiteralReason {
|
||||
Generic,
|
||||
CfgString,
|
||||
CfgBoolean,
|
||||
DeprecatedString,
|
||||
DeprecatedKvPair,
|
||||
}
|
||||
|
||||
pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
|
||||
attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
|
||||
}
|
||||
|
||||
pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
|
||||
first_attr_value_str_by_name(attrs, sym::crate_name)
|
||||
}
|
||||
|
||||
/// Parse a rustc version number written inside string literal in an attribute,
|
||||
/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
|
||||
/// not accepted in this position, unlike when parsing CFG_RELEASE.
|
||||
|
@ -34,3 +18,11 @@ pub fn parse_version(s: Symbol) -> Option<RustcVersion> {
|
|||
let patch = digits.next().unwrap_or("0").parse().ok()?;
|
||||
Some(RustcVersion { major, minor, patch })
|
||||
}
|
||||
|
||||
pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
|
||||
attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
|
||||
}
|
||||
|
||||
pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
|
||||
first_attr_value_str_by_name(attrs, sym::crate_name)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Deref;
|
||||
use std::sync::LazyLock;
|
||||
|
@ -11,7 +12,15 @@ use rustc_session::Session;
|
|||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
|
||||
|
||||
use crate::attributes::AttributeParser as _;
|
||||
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
|
||||
use crate::attributes::confusables::ConfusablesParser;
|
||||
use crate::attributes::deprecation::DeprecationParser;
|
||||
use crate::attributes::repr::ReprParser;
|
||||
use crate::attributes::stability::{
|
||||
BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
|
||||
};
|
||||
use crate::attributes::transparency::TransparencyParser;
|
||||
use crate::attributes::{AttributeParser as _, Combine, Single};
|
||||
use crate::parser::{ArgParser, MetaItemParser};
|
||||
|
||||
macro_rules! attribute_groups {
|
||||
|
@ -52,6 +61,24 @@ macro_rules! attribute_groups {
|
|||
|
||||
attribute_groups!(
|
||||
pub(crate) static ATTRIBUTE_MAPPING = [
|
||||
// tidy-alphabetical-start
|
||||
BodyStabilityParser,
|
||||
ConfusablesParser,
|
||||
ConstStabilityParser,
|
||||
StabilityParser,
|
||||
// tidy-alphabetical-end
|
||||
|
||||
// tidy-alphabetical-start
|
||||
Combine<AllowConstFnUnstableParser>,
|
||||
Combine<AllowInternalUnstableParser>,
|
||||
Combine<ReprParser>,
|
||||
// tidy-alphabetical-end
|
||||
|
||||
// tidy-alphabetical-start
|
||||
Single<ConstStabilityIndirectParser>,
|
||||
Single<DeprecationParser>,
|
||||
Single<TransparencyParser>,
|
||||
// tidy-alphabetical-end
|
||||
];
|
||||
);
|
||||
|
||||
|
|
|
@ -83,14 +83,59 @@
|
|||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
#[macro_use]
|
||||
mod attributes;
|
||||
mod context;
|
||||
pub mod parser;
|
||||
mod session_diagnostics;
|
||||
|
||||
pub use attributes::*;
|
||||
pub use attributes::cfg::*;
|
||||
pub use attributes::util::{find_crate_name, is_builtin_attr, parse_version};
|
||||
pub use context::{AttributeParser, OmitDoc};
|
||||
pub use rustc_attr_data_structures::*;
|
||||
pub use util::{find_crate_name, is_builtin_attr, parse_version};
|
||||
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
||||
/// Finds attributes in sequences of attributes by pattern matching.
|
||||
///
|
||||
/// A little like `matches` but for attributes.
|
||||
///
|
||||
/// ```rust,ignore (illustrative)
|
||||
/// // finds the repr attribute
|
||||
/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
|
||||
///
|
||||
/// }
|
||||
///
|
||||
/// // checks if one has matched
|
||||
/// if find_attr!(attrs, AttributeKind::Repr(_)) {
|
||||
///
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Often this requires you to first end up with a list of attributes.
|
||||
/// A common way to get those is through `tcx.get_all_attrs(did)`
|
||||
#[macro_export]
|
||||
macro_rules! find_attr {
|
||||
($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{
|
||||
$crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some()
|
||||
}};
|
||||
|
||||
($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
|
||||
fn check_attribute_iterator<'a>(_: &'_ impl IntoIterator<Item = &'a rustc_hir::Attribute>) {}
|
||||
check_attribute_iterator(&$attributes_list);
|
||||
|
||||
let find_attribute = |iter| {
|
||||
for i in $attributes_list {
|
||||
match i {
|
||||
rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => {
|
||||
return Some($e);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
};
|
||||
find_attribute($attributes_list)
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -6,9 +6,16 @@ use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuar
|
|||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
use crate::attributes::util::UnsupportedLiteralReason;
|
||||
use crate::fluent_generated as fluent;
|
||||
|
||||
pub(crate) enum UnsupportedLiteralReason {
|
||||
Generic,
|
||||
CfgString,
|
||||
CfgBoolean,
|
||||
DeprecatedString,
|
||||
DeprecatedKvPair,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_expected_one_cfg_pattern, code = E0536)]
|
||||
pub(crate) struct ExpectedOneCfgPattern {
|
||||
|
@ -39,6 +46,21 @@ pub(crate) struct MultipleItem {
|
|||
pub(crate) struct IncorrectMetaItem {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
|
||||
#[subdiagnostic]
|
||||
pub suggestion: Option<IncorrectMetaItemSuggestion>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
attr_parsing_incorrect_meta_item_suggestion,
|
||||
applicability = "maybe-incorrect"
|
||||
)]
|
||||
pub(crate) struct IncorrectMetaItemSuggestion {
|
||||
#[suggestion_part(code = "\"")]
|
||||
pub lo: Span,
|
||||
#[suggestion_part(code = "\"")]
|
||||
pub hi: Span,
|
||||
}
|
||||
|
||||
/// Error code: E0541
|
||||
|
@ -337,13 +359,6 @@ pub(crate) struct RustcPromotablePairing {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_rustc_const_stable_indirect_pairing)]
|
||||
pub(crate) struct RustcConstStableIndirectPairing {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_rustc_allowed_unstable_pairing, code = E0789)]
|
||||
pub(crate) struct RustcAllowedUnstablePairing {
|
||||
|
@ -423,3 +438,44 @@ pub(crate) struct UnknownVersionLiteral {
|
|||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
// FIXME(jdonszelmann) duplicated from `rustc_passes`, remove once `check_attr` is integrated.
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_unused_multiple)]
|
||||
pub(crate) struct UnusedMultiple {
|
||||
#[primary_span]
|
||||
#[suggestion(code = "", applicability = "machine-applicable")]
|
||||
pub this: Span,
|
||||
#[note]
|
||||
pub other: Span,
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_stability_outside_std, code = E0734)]
|
||||
pub(crate) struct StabilityOutsideStd {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_empty_confusables)]
|
||||
pub(crate) struct EmptyConfusables {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_repr_ident, code = E0565)]
|
||||
pub(crate) struct ReprIdent {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(attr_parsing_unrecognized_repr_hint, code = E0552)]
|
||||
#[help]
|
||||
pub(crate) struct UnrecognizedReprHint {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ rustc_errors = { path = "../rustc_errors" }
|
|||
rustc_expand = { path = "../rustc_expand" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_lexer = { path = "../rustc_lexer" }
|
||||
rustc_lint_defs = { path = "../rustc_lint_defs" }
|
||||
|
|
|
@ -185,8 +185,9 @@ use rustc_ast::{
|
|||
self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind,
|
||||
Generics, Mutability, PatKind, VariantData,
|
||||
};
|
||||
use rustc_attr_parsing as attr;
|
||||
use rustc_attr_parsing::{AttributeKind, AttributeParser, ReprPacked};
|
||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||
use rustc_hir::Attribute;
|
||||
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
|
||||
use thin_vec::{ThinVec, thin_vec};
|
||||
use ty::{Bounds, Path, Ref, Self_, Ty};
|
||||
|
@ -480,14 +481,10 @@ impl<'a> TraitDef<'a> {
|
|||
) {
|
||||
match item {
|
||||
Annotatable::Item(item) => {
|
||||
let is_packed = item.attrs.iter().any(|attr| {
|
||||
for r in attr::find_repr_attrs(cx.sess, attr) {
|
||||
if let attr::ReprPacked(_) = r {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
});
|
||||
let is_packed = matches!(
|
||||
AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, true),
|
||||
Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(x, _)| matches!(x, ReprPacked(..)))
|
||||
);
|
||||
|
||||
let newitem = match &item.kind {
|
||||
ast::ItemKind::Struct(struct_def, generics) => self.expand_struct_def(
|
||||
|
|
|
@ -5,7 +5,9 @@ use std::time::{Duration, Instant};
|
|||
|
||||
use itertools::Itertools;
|
||||
use rustc_abi::FIRST_VARIANT;
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, AllocatorKind, global_fn_name};
|
||||
use rustc_attr_parsing::OptimizeAttr;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
|
||||
use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
|
||||
use rustc_data_structures::sync::par_map;
|
||||
|
@ -29,7 +31,6 @@ use rustc_span::{DUMMY_SP, Symbol, sym};
|
|||
use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt};
|
||||
use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
|
||||
use tracing::{debug, info};
|
||||
use {rustc_ast as ast, rustc_attr_parsing as attr};
|
||||
|
||||
use crate::assert_module_sources::CguReuse;
|
||||
use crate::back::link::are_upstream_rust_objects_already_included;
|
||||
|
@ -1061,7 +1062,7 @@ pub(crate) fn provide(providers: &mut Providers) {
|
|||
|
||||
let any_for_speed = defids.items().any(|id| {
|
||||
let CodegenFnAttrs { optimize, .. } = tcx.codegen_fn_attrs(*id);
|
||||
matches!(optimize, attr::OptimizeAttr::Speed)
|
||||
matches!(optimize, OptimizeAttr::Speed)
|
||||
});
|
||||
|
||||
if any_for_speed {
|
||||
|
|
|
@ -5,7 +5,8 @@ use rustc_ast::expand::autodiff_attrs::{
|
|||
AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity,
|
||||
};
|
||||
use rustc_ast::{MetaItem, MetaItemInner, attr};
|
||||
use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr};
|
||||
use rustc_attr_parsing::ReprAttr::ReprAlign;
|
||||
use rustc_attr_parsing::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err};
|
||||
|
@ -112,6 +113,18 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
};
|
||||
|
||||
if let hir::Attribute::Parsed(p) = attr {
|
||||
match p {
|
||||
AttributeKind::Repr(reprs) => {
|
||||
codegen_fn_attrs.alignment = reprs
|
||||
.iter()
|
||||
.find_map(|(r, _)| if let ReprAlign(x) = r { Some(*x) } else { None });
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let Some(Ident { name, .. }) = attr.ident() else {
|
||||
continue;
|
||||
};
|
||||
|
@ -426,27 +439,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
}
|
||||
})
|
||||
}
|
||||
sym::repr => {
|
||||
codegen_fn_attrs.alignment = if let Some(items) = attr.meta_item_list()
|
||||
&& let [item] = items.as_slice()
|
||||
&& let Some((sym::align, literal)) = item.singleton_lit_list()
|
||||
{
|
||||
rustc_attr_parsing::parse_alignment(&literal.kind)
|
||||
.inspect_err(|msg| {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
literal.span,
|
||||
E0589,
|
||||
"invalid `repr(align)` attribute: {}",
|
||||
msg
|
||||
)
|
||||
.emit();
|
||||
})
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
sym::patchable_function_entry => {
|
||||
codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
|
||||
let mut prefix = None;
|
||||
|
@ -831,7 +823,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> {
|
|||
export_name: Some(export_name),
|
||||
no_mangle: Some(no_mangle),
|
||||
hir_id: Some(hir_id),
|
||||
no_mangle_attr: Some(no_mangle_attr),
|
||||
no_mangle_attr: Some(_),
|
||||
} = self
|
||||
{
|
||||
tcx.emit_node_span_lint(
|
||||
|
@ -840,7 +832,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> {
|
|||
no_mangle,
|
||||
errors::MixedExportNameAndNoMangle {
|
||||
no_mangle,
|
||||
no_mangle_attr: rustc_hir_pretty::attribute_to_string(&tcx, no_mangle_attr),
|
||||
no_mangle_attr: "#[unsafe(no_mangle)]".to_string(),
|
||||
export_name,
|
||||
removal_span: no_mangle,
|
||||
},
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
//! has interior mutability or needs to be dropped, as well as the visitor that emits errors when
|
||||
//! it finds operations that are invalid in a certain context.
|
||||
|
||||
use rustc_attr_parsing::{AttributeKind, find_attr};
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::ty::{self, PolyFnSig, TyCtxt};
|
||||
use rustc_middle::{bug, mir};
|
||||
use rustc_span::Symbol;
|
||||
use {rustc_attr_parsing as attr, rustc_hir as hir};
|
||||
|
||||
pub use self::qualifs::Qualif;
|
||||
|
||||
|
@ -81,7 +82,8 @@ pub fn rustc_allow_const_fn_unstable(
|
|||
feature_gate: Symbol,
|
||||
) -> bool {
|
||||
let attrs = tcx.hir().attrs(tcx.local_def_id_to_hir_id(def_id));
|
||||
attr::rustc_allow_const_fn_unstable(tcx.sess, attrs).any(|name| name == feature_gate)
|
||||
|
||||
find_attr!(attrs, AttributeKind::AllowConstFnUnstable(syms) if syms.contains(&feature_gate))
|
||||
}
|
||||
|
||||
/// Returns `true` if the given `def_id` (trait or function) is "safe to expose on stable".
|
||||
|
|
|
@ -10,6 +10,7 @@ derive_setters = "0.1.6"
|
|||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
|
||||
rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_error_codes = { path = "../rustc_error_codes" }
|
||||
rustc_error_messages = { path = "../rustc_error_messages" }
|
||||
|
|
|
@ -8,6 +8,7 @@ use std::process::ExitStatus;
|
|||
use rustc_abi::TargetDataLayoutErrors;
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr_data_structures::RustcVersion;
|
||||
use rustc_macros::Subdiagnostic;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol};
|
||||
|
@ -96,6 +97,12 @@ into_diag_arg_using_display!(
|
|||
rustc_abi::ExternAbi,
|
||||
);
|
||||
|
||||
impl IntoDiagArg for RustcVersion {
|
||||
fn into_diag_arg(self) -> DiagArgValue {
|
||||
DiagArgValue::Str(Cow::Owned(self.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: rustc_type_ir::Interner> IntoDiagArg for rustc_type_ir::TraitRef<I> {
|
||||
fn into_diag_arg(self) -> DiagArgValue {
|
||||
self.to_string().into_diag_arg()
|
||||
|
|
|
@ -17,6 +17,7 @@ rustc_data_structures = { path = "../rustc_data_structures" }
|
|||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_feature = { path = "../rustc_feature" }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_lexer = { path = "../rustc_lexer" }
|
||||
rustc_lint_defs = { path = "../rustc_lint_defs" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
|
|
|
@ -11,11 +11,12 @@ use rustc_ast::token::Nonterminal;
|
|||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::visit::{AssocCtxt, Visitor};
|
||||
use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
|
||||
use rustc_attr_parsing::{self as attr, Deprecation, Stability};
|
||||
use rustc_attr_parsing::{AttributeKind, Deprecation, Stability, find_attr};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::sync;
|
||||
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult};
|
||||
use rustc_feature::Features;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools};
|
||||
use rustc_parse::MACRO_ARGUMENTS;
|
||||
use rustc_parse::parser::Parser;
|
||||
|
@ -838,19 +839,23 @@ impl SyntaxExtension {
|
|||
/// and other properties converted from attributes.
|
||||
pub fn new(
|
||||
sess: &Session,
|
||||
features: &Features,
|
||||
kind: SyntaxExtensionKind,
|
||||
span: Span,
|
||||
helper_attrs: Vec<Symbol>,
|
||||
edition: Edition,
|
||||
name: Symbol,
|
||||
attrs: &[impl AttributeExt],
|
||||
attrs: &[hir::Attribute],
|
||||
is_local: bool,
|
||||
) -> SyntaxExtension {
|
||||
let allow_internal_unstable =
|
||||
rustc_attr_parsing::allow_internal_unstable(sess, attrs).collect::<Vec<Symbol>>();
|
||||
find_attr!(attrs, AttributeKind::AllowInternalUnstable(i) => i)
|
||||
.map(|i| i.as_slice())
|
||||
.unwrap_or_default();
|
||||
// FIXME(jdonszelman): allow_internal_unsafe isn't yet new-style
|
||||
// let allow_internal_unsafe = find_attr!(attrs, AttributeKind::AllowInternalUnsafe);
|
||||
let allow_internal_unsafe =
|
||||
ast::attr::find_by_name(attrs, sym::allow_internal_unsafe).is_some();
|
||||
|
||||
let allow_internal_unsafe = ast::attr::contains_name(attrs, sym::allow_internal_unsafe);
|
||||
let local_inner_macros = ast::attr::find_by_name(attrs, sym::macro_export)
|
||||
.and_then(|macro_export| macro_export.meta_item_list())
|
||||
.is_some_and(|l| ast::attr::list_contains_name(&l, sym::local_inner_macros));
|
||||
|
@ -867,16 +872,17 @@ impl SyntaxExtension {
|
|||
)
|
||||
})
|
||||
.unwrap_or_else(|| (None, helper_attrs));
|
||||
let stability = attr::find_stability(sess, attrs, span);
|
||||
let const_stability = attr::find_const_stability(sess, attrs, span);
|
||||
let body_stability = attr::find_body_stability(sess, attrs);
|
||||
if let Some((_, sp)) = const_stability {
|
||||
|
||||
let stability = find_attr!(attrs, AttributeKind::Stability{stability, ..} => *stability);
|
||||
|
||||
// FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem
|
||||
if let Some(sp) = find_attr!(attrs, AttributeKind::ConstStability{span, ..} => *span) {
|
||||
sess.dcx().emit_err(errors::MacroConstStability {
|
||||
span: sp,
|
||||
head_span: sess.source_map().guess_head_span(span),
|
||||
});
|
||||
}
|
||||
if let Some((_, sp)) = body_stability {
|
||||
if let Some(sp) = find_attr!(attrs, AttributeKind::BodyStability{span, ..} => *span) {
|
||||
sess.dcx().emit_err(errors::MacroBodyStability {
|
||||
span: sp,
|
||||
head_span: sess.source_map().guess_head_span(span),
|
||||
|
@ -887,9 +893,10 @@ impl SyntaxExtension {
|
|||
kind,
|
||||
span,
|
||||
allow_internal_unstable: (!allow_internal_unstable.is_empty())
|
||||
.then(|| allow_internal_unstable.into()),
|
||||
stability: stability.map(|(s, _)| s),
|
||||
deprecation: attr::find_deprecation(sess, features, attrs).map(|(d, _)| d),
|
||||
// FIXME(jdonszelmann): avoid the into_iter/collect?
|
||||
.then(|| allow_internal_unstable.iter().map(|i| i.0).collect::<Vec<_>>().into()),
|
||||
stability,
|
||||
deprecation: find_attr!(attrs, AttributeKind::Deprecation{deprecation, ..} => *deprecation),
|
||||
helper_attrs,
|
||||
edition,
|
||||
builtin_name,
|
||||
|
|
|
@ -3,17 +3,17 @@ use std::collections::hash_map::Entry;
|
|||
use std::{mem, slice};
|
||||
|
||||
use ast::token::IdentIsRaw;
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_ast::token::NtPatKind::*;
|
||||
use rustc_ast::token::TokenKind::*;
|
||||
use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
|
||||
use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr_parsing::{self as attr, TransparencyError};
|
||||
use rustc_attr_parsing::{AttributeKind, find_attr};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
use rustc_errors::{Applicability, ErrorGuaranteed};
|
||||
use rustc_feature::Features;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint_defs::BuiltinLintDiag;
|
||||
use rustc_lint_defs::builtin::{
|
||||
RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
|
||||
|
@ -371,7 +371,7 @@ pub fn compile_declarative_macro(
|
|||
features: &Features,
|
||||
macro_def: &ast::MacroDef,
|
||||
ident: Ident,
|
||||
attrs: &[impl AttributeExt],
|
||||
attrs: &[hir::Attribute],
|
||||
span: Span,
|
||||
node_id: NodeId,
|
||||
edition: Edition,
|
||||
|
@ -379,7 +379,6 @@ pub fn compile_declarative_macro(
|
|||
let mk_syn_ext = |expander| {
|
||||
SyntaxExtension::new(
|
||||
sess,
|
||||
features,
|
||||
SyntaxExtensionKind::LegacyBang(expander),
|
||||
span,
|
||||
Vec::new(),
|
||||
|
@ -391,7 +390,6 @@ pub fn compile_declarative_macro(
|
|||
};
|
||||
let dummy_syn_ext = |guar| (mk_syn_ext(Box::new(DummyExpander(guar))), Vec::new());
|
||||
|
||||
let dcx = sess.dcx();
|
||||
let lhs_nm = Ident::new(sym::lhs, span);
|
||||
let rhs_nm = Ident::new(sym::rhs, span);
|
||||
let tt_spec = Some(NonterminalKind::TT);
|
||||
|
@ -542,16 +540,8 @@ pub fn compile_declarative_macro(
|
|||
|
||||
check_emission(macro_check::check_meta_variables(&sess.psess, node_id, span, &lhses, &rhses));
|
||||
|
||||
let (transparency, transparency_error) = attr::find_transparency(attrs, macro_rules);
|
||||
match transparency_error {
|
||||
Some(TransparencyError::UnknownTransparency(value, span)) => {
|
||||
dcx.span_err(span, format!("unknown macro transparency: `{value}`"));
|
||||
}
|
||||
Some(TransparencyError::MultipleTransparencyAttrs(old_span, new_span)) => {
|
||||
dcx.span_err(vec![old_span, new_span], "multiple macro transparency attributes");
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
let transparency = find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x)
|
||||
.unwrap_or(Transparency::fallback(macro_rules));
|
||||
|
||||
if let Some(guar) = guar {
|
||||
// To avoid warning noise, only consider the rules of this
|
||||
|
|
|
@ -1151,6 +1151,9 @@ impl AttributeExt for Attribute {
|
|||
fn span(&self) -> Span {
|
||||
match &self {
|
||||
Attribute::Unparsed(u) => u.span,
|
||||
// FIXME: should not be needed anymore when all attrs are parsed
|
||||
Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span,
|
||||
Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
|
||||
a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"),
|
||||
}
|
||||
}
|
||||
|
@ -1193,6 +1196,7 @@ impl AttributeExt for Attribute {
|
|||
fn style(&self) -> AttrStyle {
|
||||
match &self {
|
||||
Attribute::Unparsed(u) => u.style,
|
||||
Attribute::Parsed(AttributeKind::DocComment { style, .. }) => *style,
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::cell::LazyCell;
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use rustc_abi::FieldIdx;
|
||||
use rustc_attr_parsing::AttributeKind;
|
||||
use rustc_attr_parsing::ReprAttr::ReprPacked;
|
||||
use rustc_data_structures::unord::{UnordMap, UnordSet};
|
||||
use rustc_errors::MultiSpan;
|
||||
use rustc_errors::codes::*;
|
||||
|
@ -1203,11 +1205,13 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
|
|||
pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) {
|
||||
let repr = def.repr();
|
||||
if repr.packed() {
|
||||
for attr in tcx.get_attrs(def.did(), sym::repr) {
|
||||
for r in attr::parse_repr_attr(tcx.sess, attr) {
|
||||
if let attr::ReprPacked(pack) = r
|
||||
if let Some(reprs) =
|
||||
attr::find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::Repr(r) => r)
|
||||
{
|
||||
for (r, _) in reprs {
|
||||
if let ReprPacked(pack) = r
|
||||
&& let Some(repr_pack) = repr.pack
|
||||
&& pack != repr_pack
|
||||
&& pack != &repr_pack
|
||||
{
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
|
@ -1419,16 +1423,19 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
|||
def.destructor(tcx); // force the destructor to be evaluated
|
||||
|
||||
if def.variants().is_empty() {
|
||||
if let Some(attr) = tcx.get_attrs(def_id, sym::repr).next() {
|
||||
attr::find_attr!(
|
||||
tcx.get_all_attrs(def_id),
|
||||
AttributeKind::Repr(rs) => {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
attr.span(),
|
||||
rs.first().unwrap().1,
|
||||
E0084,
|
||||
"unsupported representation for zero-variant enum"
|
||||
)
|
||||
.with_span_label(tcx.def_span(def_id), "zero-variant enum")
|
||||
.emit();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let repr_type_ty = def.repr().discr_type().to_ty(tcx);
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::path::PathBuf;
|
|||
|
||||
use hir::Expr;
|
||||
use rustc_ast::ast::Mutability;
|
||||
use rustc_attr_parsing::parse_confusables;
|
||||
use rustc_attr_parsing::{AttributeKind, find_attr};
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
|
@ -1884,9 +1884,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
for inherent_method in
|
||||
self.tcx.associated_items(inherent_impl_did).in_definition_order()
|
||||
{
|
||||
if let Some(attr) =
|
||||
self.tcx.get_attr(inherent_method.def_id, sym::rustc_confusables)
|
||||
&& let Some(candidates) = parse_confusables(attr)
|
||||
if let Some(candidates) = find_attr!(self.tcx.get_all_attrs(inherent_method.def_id), AttributeKind::Confusables{symbols, ..} => symbols)
|
||||
&& candidates.contains(&item_name.name)
|
||||
&& let ty::AssocKind::Fn = inherent_method.kind
|
||||
{
|
||||
|
|
|
@ -39,6 +39,7 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
|
|||
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
|
||||
// We are an `eval_always` query, so looking at the attribute's `AttrId` is ok.
|
||||
let attr_id = tcx.hir().attrs(hir_id)[attr_index as usize].id();
|
||||
|
||||
(attr_id, lint_index)
|
||||
}
|
||||
_ => panic!("fulfilled expectations must have a lint index"),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use rustc_abi::ExternAbi;
|
||||
use rustc_attr_parsing::{AttributeKind, AttributeParser, ReprAttr};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatExprKind, PatKind};
|
||||
|
@ -7,7 +8,7 @@ use rustc_session::config::CrateType;
|
|||
use rustc_session::{declare_lint, declare_lint_pass};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{BytePos, Ident, Span, sym};
|
||||
use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir};
|
||||
use {rustc_ast as ast, rustc_hir as hir};
|
||||
|
||||
use crate::lints::{
|
||||
NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub,
|
||||
|
@ -161,10 +162,10 @@ impl NonCamelCaseTypes {
|
|||
|
||||
impl EarlyLintPass for NonCamelCaseTypes {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
|
||||
let has_repr_c = it
|
||||
.attrs
|
||||
.iter()
|
||||
.any(|attr| attr::find_repr_attrs(cx.sess(), attr).contains(&attr::ReprC));
|
||||
let has_repr_c = matches!(
|
||||
AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, true),
|
||||
Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(r, _)| r == &ReprAttr::ReprC)
|
||||
);
|
||||
|
||||
if has_repr_c {
|
||||
return;
|
||||
|
|
|
@ -251,19 +251,23 @@ impl Level {
|
|||
|
||||
/// Converts an `Attribute` to a level.
|
||||
pub fn from_attr(attr: &impl AttributeExt) -> Option<Self> {
|
||||
Self::from_symbol(attr.name_or_empty(), Some(attr.id()))
|
||||
Self::from_symbol(attr.name_or_empty(), || Some(attr.id()))
|
||||
}
|
||||
|
||||
/// Converts a `Symbol` to a level.
|
||||
pub fn from_symbol(s: Symbol, id: Option<AttrId>) -> Option<Self> {
|
||||
match (s, id) {
|
||||
(sym::allow, _) => Some(Level::Allow),
|
||||
(sym::expect, Some(attr_id)) => {
|
||||
pub fn from_symbol(s: Symbol, id: impl FnOnce() -> Option<AttrId>) -> Option<Self> {
|
||||
match s {
|
||||
sym::allow => Some(Level::Allow),
|
||||
sym::expect => {
|
||||
if let Some(attr_id) = id() {
|
||||
Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
(sym::warn, _) => Some(Level::Warn),
|
||||
(sym::deny, _) => Some(Level::Deny),
|
||||
(sym::forbid, _) => Some(Level::Forbid),
|
||||
}
|
||||
sym::warn => Some(Level::Warn),
|
||||
sym::deny => Some(Level::Deny),
|
||||
sym::forbid => Some(Level::Forbid),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1071,7 +1071,6 @@ impl<'a> CrateMetadataRef<'a> {
|
|||
let attrs: Vec<_> = self.get_item_attrs(id, sess).collect();
|
||||
SyntaxExtension::new(
|
||||
sess,
|
||||
tcx.features(),
|
||||
kind,
|
||||
self.get_span(id, sess),
|
||||
helper_attrs,
|
||||
|
|
|
@ -27,6 +27,7 @@ pub use intrinsic::IntrinsicDef;
|
|||
use rustc_abi::{Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, VariantIdx};
|
||||
use rustc_ast::expand::StrippedCfgItem;
|
||||
use rustc_ast::node_id::NodeMap;
|
||||
use rustc_attr_parsing::AttributeKind;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
|
@ -1495,9 +1496,10 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
field_shuffle_seed ^= user_seed;
|
||||
}
|
||||
|
||||
for attr in self.get_attrs(did, sym::repr) {
|
||||
for r in attr::parse_repr_attr(self.sess, attr) {
|
||||
flags.insert(match r {
|
||||
if let Some(reprs) = attr::find_attr!(self.get_all_attrs(did), AttributeKind::Repr(r) => r)
|
||||
{
|
||||
for (r, _) in reprs {
|
||||
flags.insert(match *r {
|
||||
attr::ReprRust => ReprFlags::empty(),
|
||||
attr::ReprC => ReprFlags::IS_C,
|
||||
attr::ReprPacked(pack) => {
|
||||
|
@ -1535,6 +1537,10 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
max_align = max_align.max(Some(align));
|
||||
ReprFlags::empty()
|
||||
}
|
||||
attr::ReprEmpty => {
|
||||
/* skip these, they're just for diagnostics */
|
||||
ReprFlags::empty()
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1756,14 +1762,22 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
self,
|
||||
did: impl Into<DefId>,
|
||||
attr: Symbol,
|
||||
) -> impl Iterator<Item = &'tcx hir::Attribute> {
|
||||
self.get_all_attrs(did).filter(move |a: &&hir::Attribute| a.has_name(attr))
|
||||
}
|
||||
|
||||
/// Gets all attributes.
|
||||
///
|
||||
/// To see if an item has a specific attribute, you should use [`rustc_attr_parsing::find_attr!`] so you can use matching.
|
||||
pub fn get_all_attrs(
|
||||
self,
|
||||
did: impl Into<DefId>,
|
||||
) -> impl Iterator<Item = &'tcx hir::Attribute> {
|
||||
let did: DefId = did.into();
|
||||
let filter_fn = move |a: &&hir::Attribute| a.has_name(attr);
|
||||
if let Some(did) = did.as_local() {
|
||||
self.hir().attrs(self.local_def_id_to_hir_id(did)).iter().filter(filter_fn)
|
||||
self.hir().attrs(self.local_def_id_to_hir_id(did)).iter()
|
||||
} else {
|
||||
debug_assert!(rustc_feature::encode_cross_crate(attr));
|
||||
self.attrs_for_def(did).iter().filter(filter_fn)
|
||||
self.attrs_for_def(did).iter()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -311,9 +311,6 @@ passes_duplicate_lang_item_crate_depends =
|
|||
.first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path}
|
||||
.second_definition_path = second definition in `{$crate_name}` loaded from {$path}
|
||||
|
||||
passes_empty_confusables =
|
||||
expected at least one confusable name
|
||||
|
||||
passes_export_name =
|
||||
attribute should be applied to a free function, impl method or static
|
||||
.label = not a free function, impl method or static
|
||||
|
@ -365,9 +362,6 @@ passes_incorrect_do_not_recommend_args =
|
|||
passes_incorrect_do_not_recommend_location =
|
||||
`#[diagnostic::do_not_recommend]` can only be placed on trait implementations
|
||||
|
||||
passes_incorrect_meta_item = expected a quoted string literal
|
||||
passes_incorrect_meta_item_suggestion = consider surrounding this with quotes
|
||||
|
||||
passes_incorrect_target =
|
||||
`{$name}` lang item must be applied to a {$kind} with {$at_least ->
|
||||
[true] at least {$num}
|
||||
|
@ -641,13 +635,12 @@ passes_repr_align_greater_than_target_max =
|
|||
passes_repr_conflicting =
|
||||
conflicting representation hints
|
||||
|
||||
passes_repr_ident =
|
||||
meta item in `repr` must be an identifier
|
||||
|
||||
passes_rustc_allow_const_fn_unstable =
|
||||
attribute should be applied to `const fn`
|
||||
.label = not a `const fn`
|
||||
|
||||
passes_rustc_const_stable_indirect_pairing =
|
||||
`const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied
|
||||
passes_rustc_dirty_clean =
|
||||
attribute requires -Z query-dep-graph to be enabled
|
||||
|
||||
|
@ -774,10 +767,6 @@ passes_unreachable_due_to_uninhabited = unreachable {$descr}
|
|||
passes_unrecognized_field =
|
||||
unrecognized field name `{$name}`
|
||||
|
||||
passes_unrecognized_repr_hint =
|
||||
unrecognized representation hint
|
||||
.help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
|
||||
|
||||
passes_unstable_attr_for_already_stable_feature =
|
||||
can't mark as unstable using an already stable feature
|
||||
.label = this feature is already stable
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// FIXME(jdonszelmann): should become rustc_attr_validation
|
||||
//! This module implements some validity checks for attributes.
|
||||
//! In particular it verifies that `#[inline]` and `#[repr]` attributes are
|
||||
//! attached to items that actually support them and if there are
|
||||
|
@ -7,8 +8,9 @@
|
|||
use std::cell::Cell;
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
use rustc_abi::{ExternAbi, Size};
|
||||
use rustc_abi::{Align, ExternAbi, Size};
|
||||
use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast};
|
||||
use rustc_attr_parsing::{AttributeKind, ReprAttr, find_attr};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey};
|
||||
use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
|
||||
|
@ -113,6 +115,23 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
let mut seen = FxHashMap::default();
|
||||
let attrs = self.tcx.hir().attrs(hir_id);
|
||||
for attr in attrs {
|
||||
match attr {
|
||||
Attribute::Parsed(AttributeKind::Confusables { first_span, .. }) => {
|
||||
self.check_confusables(*first_span, target);
|
||||
}
|
||||
Attribute::Parsed(
|
||||
AttributeKind::Stability { span, .. }
|
||||
| AttributeKind::ConstStability { span, .. },
|
||||
) => self.check_stability_promotable(*span, target),
|
||||
Attribute::Parsed(AttributeKind::AllowInternalUnstable(syms)) => self
|
||||
.check_allow_internal_unstable(
|
||||
hir_id,
|
||||
syms.first().unwrap().1,
|
||||
span,
|
||||
target,
|
||||
attrs,
|
||||
),
|
||||
_ => {
|
||||
match attr.path().as_slice() {
|
||||
[sym::diagnostic, sym::do_not_recommend, ..] => {
|
||||
self.check_do_not_recommend(attr.span(), hir_id, target, attr, item)
|
||||
|
@ -123,7 +142,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
[sym::inline, ..] => self.check_inline(hir_id, attr, span, target),
|
||||
[sym::coverage, ..] => self.check_coverage(attr, span, target),
|
||||
[sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target),
|
||||
[sym::no_sanitize, ..] => self.check_no_sanitize(attr, span, target),
|
||||
[sym::no_sanitize, ..] => {
|
||||
self.check_no_sanitize(attr, span, target)
|
||||
}
|
||||
[sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target, item),
|
||||
[sym::marker, ..] => self.check_marker(hir_id, attr, span, target),
|
||||
[sym::target_feature, ..] => {
|
||||
|
@ -146,9 +167,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
| [sym::rustc_layout_scalar_valid_range_end, ..] => {
|
||||
self.check_rustc_layout_scalar_valid_range(attr, span, target)
|
||||
}
|
||||
[sym::allow_internal_unstable, ..] => {
|
||||
self.check_allow_internal_unstable(hir_id, attr, span, target, attrs)
|
||||
}
|
||||
[sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target),
|
||||
[sym::rustc_allow_const_fn_unstable, ..] => {
|
||||
self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target)
|
||||
|
@ -201,14 +219,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
[sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target),
|
||||
[sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
|
||||
[sym::rustc_const_unstable, ..]
|
||||
| [sym::rustc_const_stable, ..]
|
||||
| [sym::unstable, ..]
|
||||
| [sym::stable, ..]
|
||||
| [sym::rustc_allowed_through_unstable_modules, ..]
|
||||
| [sym::rustc_promotable, ..] => self.check_stability_promotable(attr, target),
|
||||
[sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target),
|
||||
[sym::rustc_confusables, ..] => self.check_confusables(attr, target),
|
||||
[sym::cold, ..] => self.check_cold(hir_id, attr, span, target),
|
||||
[sym::link, ..] => self.check_link(hir_id, attr, span, target),
|
||||
[sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target),
|
||||
|
@ -298,6 +309,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
[] => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
|
||||
|
||||
|
@ -343,11 +356,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
|
||||
fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr_span: Span, sym: &str) {
|
||||
self.tcx.emit_node_span_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span(),
|
||||
attr_span,
|
||||
errors::IgnoredAttr { sym },
|
||||
);
|
||||
}
|
||||
|
@ -568,6 +581,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
sym::warn,
|
||||
sym::deny,
|
||||
sym::forbid,
|
||||
// FIXME(jdonszelmann): not used, because already a new-style attr (ugh)
|
||||
sym::deprecated,
|
||||
sym::must_use,
|
||||
// abi, linking and FFI
|
||||
|
@ -595,6 +609,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
continue;
|
||||
}
|
||||
|
||||
// FIXME(jdonszelmann): once naked uses new-style parsing,
|
||||
// this check can be part of the parser and be removed here
|
||||
match other_attr {
|
||||
Attribute::Parsed(
|
||||
AttributeKind::Deprecation { .. } | AttributeKind::Repr { .. },
|
||||
) => {
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if !ALLOW_LIST.iter().any(|name| other_attr.has_name(*name)) {
|
||||
self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute {
|
||||
span: other_attr.span(),
|
||||
|
@ -1858,12 +1883,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
// #[repr(foo)]
|
||||
// #[repr(bar, align(8))]
|
||||
// ```
|
||||
let hints: Vec<_> = attrs
|
||||
.iter()
|
||||
.filter(|attr| attr.has_name(sym::repr))
|
||||
.filter_map(|attr| attr.meta_item_list())
|
||||
.flatten()
|
||||
.collect();
|
||||
let reprs = find_attr!(attrs, AttributeKind::Repr(r) => r.as_slice()).unwrap_or(&[]);
|
||||
|
||||
let mut int_reprs = 0;
|
||||
let mut is_explicit_rust = false;
|
||||
|
@ -1871,16 +1891,114 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
let mut is_simd = false;
|
||||
let mut is_transparent = false;
|
||||
|
||||
for (repr, repr_span) in reprs {
|
||||
match repr {
|
||||
ReprAttr::ReprRust => {
|
||||
is_explicit_rust = true;
|
||||
match target {
|
||||
Target::Struct | Target::Union | Target::Enum => continue,
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
|
||||
hint_span: *repr_span,
|
||||
span,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
ReprAttr::ReprC => {
|
||||
is_c = true;
|
||||
match target {
|
||||
Target::Struct | Target::Union | Target::Enum => continue,
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
|
||||
hint_span: *repr_span,
|
||||
span,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
ReprAttr::ReprAlign(align) => {
|
||||
match target {
|
||||
Target::Struct | Target::Union | Target::Enum => {}
|
||||
Target::Fn | Target::Method(_) => {
|
||||
if !self.tcx.features().fn_align() {
|
||||
feature_err(
|
||||
&self.tcx.sess,
|
||||
sym::fn_align,
|
||||
*repr_span,
|
||||
fluent::passes_repr_align_function,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.dcx().emit_err(
|
||||
errors::AttrApplication::StructEnumFunctionMethodUnion {
|
||||
hint_span: *repr_span,
|
||||
span,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.check_align_value(*align, *repr_span);
|
||||
}
|
||||
ReprAttr::ReprPacked(_) => {
|
||||
if target != Target::Struct && target != Target::Union {
|
||||
self.dcx().emit_err(errors::AttrApplication::StructUnion {
|
||||
hint_span: *repr_span,
|
||||
span,
|
||||
});
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ReprAttr::ReprSimd => {
|
||||
is_simd = true;
|
||||
if target != Target::Struct {
|
||||
self.dcx().emit_err(errors::AttrApplication::Struct {
|
||||
hint_span: *repr_span,
|
||||
span,
|
||||
});
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ReprAttr::ReprTransparent => {
|
||||
is_transparent = true;
|
||||
match target {
|
||||
Target::Struct | Target::Union | Target::Enum => continue,
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
|
||||
hint_span: *repr_span,
|
||||
span,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
ReprAttr::ReprInt(_) => {
|
||||
int_reprs += 1;
|
||||
if target != Target::Enum {
|
||||
self.dcx().emit_err(errors::AttrApplication::Enum {
|
||||
hint_span: *repr_span,
|
||||
span,
|
||||
});
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// FIXME(jdonszelmann): move the diagnostic for unused repr attrs here, I think
|
||||
// it's a better place for it.
|
||||
ReprAttr::ReprEmpty => {
|
||||
// catch `repr()` with no arguments, applied to an item (i.e. not `#![repr()]`)
|
||||
if hints.is_empty() && item.is_some() {
|
||||
for attr in attrs.iter().filter(|attr| attr.has_name(sym::repr)) {
|
||||
if item.is_some() {
|
||||
match target {
|
||||
Target::Struct | Target::Union | Target::Enum => {}
|
||||
Target::Fn | Target::Method(_) => {
|
||||
feature_err(
|
||||
&self.tcx.sess,
|
||||
sym::fn_align,
|
||||
attr.span(),
|
||||
*repr_span,
|
||||
fluent::passes_repr_align_function,
|
||||
)
|
||||
.emit();
|
||||
|
@ -1888,7 +2006,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
_ => {
|
||||
self.dcx().emit_err(
|
||||
errors::AttrApplication::StructEnumFunctionMethodUnion {
|
||||
hint_span: attr.span(),
|
||||
hint_span: *repr_span,
|
||||
span,
|
||||
},
|
||||
);
|
||||
|
@ -1898,132 +2016,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
for hint in &hints {
|
||||
if !hint.is_meta_item() {
|
||||
self.dcx().emit_err(errors::ReprIdent { span: hint.span() });
|
||||
continue;
|
||||
}
|
||||
|
||||
match hint.name_or_empty() {
|
||||
sym::Rust => {
|
||||
is_explicit_rust = true;
|
||||
match target {
|
||||
Target::Struct | Target::Union | Target::Enum => continue,
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
|
||||
hint_span: hint.span(),
|
||||
span,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
sym::C => {
|
||||
is_c = true;
|
||||
match target {
|
||||
Target::Struct | Target::Union | Target::Enum => continue,
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
|
||||
hint_span: hint.span(),
|
||||
span,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
sym::align => {
|
||||
match target {
|
||||
Target::Struct | Target::Union | Target::Enum => {}
|
||||
Target::Fn | Target::Method(_) => {
|
||||
if !self.tcx.features().fn_align() {
|
||||
feature_err(
|
||||
&self.tcx.sess,
|
||||
sym::fn_align,
|
||||
hint.span(),
|
||||
fluent::passes_repr_align_function,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.dcx().emit_err(
|
||||
errors::AttrApplication::StructEnumFunctionMethodUnion {
|
||||
hint_span: hint.span(),
|
||||
span,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.check_align_value(hint);
|
||||
}
|
||||
sym::packed => {
|
||||
if target != Target::Struct && target != Target::Union {
|
||||
self.dcx().emit_err(errors::AttrApplication::StructUnion {
|
||||
hint_span: hint.span(),
|
||||
span,
|
||||
});
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
sym::simd => {
|
||||
is_simd = true;
|
||||
if target != Target::Struct {
|
||||
self.dcx().emit_err(errors::AttrApplication::Struct {
|
||||
hint_span: hint.span(),
|
||||
span,
|
||||
});
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
sym::transparent => {
|
||||
is_transparent = true;
|
||||
match target {
|
||||
Target::Struct | Target::Union | Target::Enum => continue,
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
|
||||
hint_span: hint.span(),
|
||||
span,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
sym::i8
|
||||
| sym::u8
|
||||
| sym::i16
|
||||
| sym::u16
|
||||
| sym::i32
|
||||
| sym::u32
|
||||
| sym::i64
|
||||
| sym::u64
|
||||
| sym::i128
|
||||
| sym::u128
|
||||
| sym::isize
|
||||
| sym::usize => {
|
||||
int_reprs += 1;
|
||||
if target != Target::Enum {
|
||||
self.dcx().emit_err(errors::AttrApplication::Enum {
|
||||
hint_span: hint.span(),
|
||||
span,
|
||||
});
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::UnrecognizedReprHint { span: hint.span() });
|
||||
continue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Just point at all repr hints if there are any incompatibilities.
|
||||
// This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
|
||||
let hint_spans = hints.iter().map(|hint| hint.span());
|
||||
let hint_spans = reprs.iter().map(|(_, span)| *span);
|
||||
|
||||
// Error on repr(transparent, <anything else>).
|
||||
if is_transparent && hints.len() > 1 {
|
||||
if is_transparent && reprs.len() > 1 {
|
||||
let hint_spans = hint_spans.clone().collect();
|
||||
self.dcx().emit_err(errors::TransparentIncompatible {
|
||||
hint_spans,
|
||||
|
@ -2052,19 +2053,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_align_value(&self, item: &MetaItemInner) {
|
||||
match item.singleton_lit_list() {
|
||||
Some((
|
||||
_,
|
||||
MetaItemLit {
|
||||
kind: ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed), ..
|
||||
},
|
||||
)) => {
|
||||
let val = literal.get() as u64;
|
||||
if val > 2_u64.pow(29) {
|
||||
fn check_align_value(&self, align: Align, span: Span) {
|
||||
if align.bytes() > 2_u64.pow(29) {
|
||||
// for values greater than 2^29, a different error will be emitted, make sure that happens
|
||||
self.dcx().span_delayed_bug(
|
||||
item.span(),
|
||||
span,
|
||||
"alignment greater than 2^29 should be errored on elsewhere",
|
||||
);
|
||||
} else {
|
||||
|
@ -2072,21 +2065,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
// alignment greater than 2^29 not supported
|
||||
// alignment is too large for the current target
|
||||
|
||||
let max =
|
||||
Size::from_bits(self.tcx.sess.target.pointer_width).signed_int_max() as u64;
|
||||
if val > max {
|
||||
self.dcx().emit_err(errors::InvalidReprAlignForTarget {
|
||||
span: item.span(),
|
||||
size: max,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the attribute is malformed, singleton_lit_list may not be of the expected type or may be None
|
||||
// but an error will have already been emitted, so this code should just skip such attributes
|
||||
Some((_, _)) | None => {
|
||||
self.dcx().span_delayed_bug(item.span(), "malformed repr(align(N))");
|
||||
let max = Size::from_bits(self.tcx.sess.target.pointer_width).signed_int_max() as u64;
|
||||
if align.bytes() > max {
|
||||
self.dcx().emit_err(errors::InvalidReprAlignForTarget { span, size: max });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2134,41 +2115,44 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
|
||||
/// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
|
||||
/// (Allows proc_macro functions)
|
||||
// FIXME(jdonszelmann): if possible, move to attr parsing
|
||||
fn check_allow_internal_unstable(
|
||||
&self,
|
||||
hir_id: HirId,
|
||||
attr: &Attribute,
|
||||
attr_span: Span,
|
||||
span: Span,
|
||||
target: Target,
|
||||
attrs: &[Attribute],
|
||||
) {
|
||||
debug!("Checking target: {:?}", target);
|
||||
match target {
|
||||
Target::Fn => {
|
||||
for attr in attrs {
|
||||
if attr.is_proc_macro_attr() {
|
||||
debug!("Is proc macro attr");
|
||||
// return on proc macros
|
||||
return;
|
||||
}
|
||||
}
|
||||
debug!("Is not proc macro attr");
|
||||
// continue out of the match
|
||||
}
|
||||
Target::MacroDef => {}
|
||||
// return on decl macros
|
||||
Target::MacroDef => return,
|
||||
// FIXME(#80564): We permit struct fields and match arms to have an
|
||||
// `#[allow_internal_unstable]` attribute with just a lint, because we previously
|
||||
// erroneously allowed it and some crates used it accidentally, to be compatible
|
||||
// with crates depending on them, we can't throw an error here.
|
||||
Target::Field | Target::Arm => self.inline_attr_str_error_without_macro_def(
|
||||
Target::Field | Target::Arm => {
|
||||
self.inline_attr_str_error_without_macro_def(
|
||||
hir_id,
|
||||
attr,
|
||||
attr_span,
|
||||
"allow_internal_unstable",
|
||||
),
|
||||
_ => {
|
||||
self.tcx
|
||||
.dcx()
|
||||
.emit_err(errors::AllowInternalUnstable { attr_span: attr.span(), span });
|
||||
);
|
||||
return;
|
||||
}
|
||||
// otherwise continue out of the match
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.tcx.dcx().emit_err(errors::AllowInternalUnstable { attr_span, span });
|
||||
}
|
||||
|
||||
/// Checks if the items on the `#[debugger_visualizer]` attribute are valid.
|
||||
|
@ -2223,10 +2207,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_stability_promotable(&self, attr: &Attribute, target: Target) {
|
||||
fn check_stability_promotable(&self, span: Span, target: Target) {
|
||||
match target {
|
||||
Target::Expression => {
|
||||
self.dcx().emit_err(errors::StabilityPromotable { attr_span: attr.span() });
|
||||
self.dcx().emit_err(errors::StabilityPromotable { attr_span: span });
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -2241,36 +2225,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_confusables(&self, attr: &Attribute, target: Target) {
|
||||
match target {
|
||||
Target::Method(MethodKind::Inherent) => {
|
||||
let Some(metas) = attr.meta_item_list() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut candidates = Vec::new();
|
||||
|
||||
for meta in metas {
|
||||
let MetaItemInner::Lit(meta_lit) = meta else {
|
||||
self.dcx().emit_err(errors::IncorrectMetaItem {
|
||||
span: meta.span(),
|
||||
suggestion: errors::IncorrectMetaItemSuggestion {
|
||||
lo: meta.span().shrink_to_lo(),
|
||||
hi: meta.span().shrink_to_hi(),
|
||||
},
|
||||
});
|
||||
return;
|
||||
};
|
||||
candidates.push(meta_lit.symbol);
|
||||
}
|
||||
|
||||
if candidates.is_empty() {
|
||||
self.dcx().emit_err(errors::EmptyConfusables { span: attr.span() });
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::Confusables { attr_span: attr.span() });
|
||||
}
|
||||
fn check_confusables(&self, span: Span, target: Target) {
|
||||
if !matches!(target, Target::Method(MethodKind::Inherent)) {
|
||||
self.dcx().emit_err(errors::Confusables { attr_span: span });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2346,8 +2303,32 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
|
||||
fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) {
|
||||
// FIXME(jdonszelmann): deduplicate these checks after more attrs are parsed. This is very
|
||||
// ugly now but can 100% be removed later.
|
||||
if let Attribute::Parsed(p) = attr {
|
||||
match p {
|
||||
AttributeKind::Repr(reprs) => {
|
||||
for (r, span) in reprs {
|
||||
if let ReprAttr::ReprEmpty = r {
|
||||
self.tcx.emit_node_span_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
*span,
|
||||
errors::Unused {
|
||||
attr_span: *span,
|
||||
note: errors::UnusedNote::EmptyList { name: sym::repr },
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Warn on useless empty attributes.
|
||||
let note = if matches!(
|
||||
let note = if (matches!(
|
||||
attr.name_or_empty(),
|
||||
sym::macro_use
|
||||
| sym::allow
|
||||
|
@ -2356,9 +2337,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
| sym::deny
|
||||
| sym::forbid
|
||||
| sym::feature
|
||||
| sym::repr
|
||||
| sym::target_feature
|
||||
) && attr.meta_item_list().is_some_and(|list| list.is_empty())
|
||||
) && attr.meta_item_list().is_some_and(|list| list.is_empty()))
|
||||
{
|
||||
errors::UnusedNote::EmptyList { name: attr.name_or_empty() }
|
||||
} else if matches!(
|
||||
|
@ -2552,12 +2532,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
|
||||
fn check_rustc_pub_transparent(&self, attr_span: Span, span: Span, attrs: &[Attribute]) {
|
||||
if !attrs
|
||||
.iter()
|
||||
.filter(|attr| attr.has_name(sym::repr))
|
||||
.filter_map(|attr| attr.meta_item_list())
|
||||
.flatten()
|
||||
.any(|nmi| nmi.has_name(sym::transparent))
|
||||
if !find_attr!(attrs, AttributeKind::Repr(r) => r.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
self.dcx().emit_err(errors::RustcPubTransparent { span, attr_span });
|
||||
}
|
||||
|
@ -2734,7 +2710,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
|
|||
// resolution for the attribute macro error.
|
||||
const ATTRS_TO_CHECK: &[Symbol] = &[
|
||||
sym::macro_export,
|
||||
sym::repr,
|
||||
sym::path,
|
||||
sym::automatically_derived,
|
||||
sym::rustc_main,
|
||||
|
@ -2746,11 +2721,18 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
|
|||
];
|
||||
|
||||
for attr in attrs {
|
||||
// This function should only be called with crate attributes
|
||||
// which are inner attributes always but lets check to make sure
|
||||
if attr.style() == AttrStyle::Inner {
|
||||
for attr_to_check in ATTRS_TO_CHECK {
|
||||
if attr.has_name(*attr_to_check) {
|
||||
// FIXME(jdonszelmann): all attrs should be combined here cleaning this up some day.
|
||||
let (span, name) = if let Some(a) =
|
||||
ATTRS_TO_CHECK.iter().find(|attr_to_check| attr.has_name(**attr_to_check))
|
||||
{
|
||||
(attr.span(), *a)
|
||||
} else if let Attribute::Parsed(AttributeKind::Repr(r)) = attr {
|
||||
(r.first().unwrap().1, sym::repr)
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
|
||||
let item = tcx
|
||||
.hir_free_items()
|
||||
.map(|id| tcx.hir_item(id))
|
||||
|
@ -2760,19 +2742,15 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
|
|||
kind: item.kind.descr(),
|
||||
});
|
||||
let err = tcx.dcx().create_err(errors::InvalidAttrAtCrateLevel {
|
||||
span: attr.span(),
|
||||
span,
|
||||
sugg_span: tcx
|
||||
.sess
|
||||
.source_map()
|
||||
.span_to_snippet(attr.span())
|
||||
.span_to_snippet(span)
|
||||
.ok()
|
||||
.filter(|src| src.starts_with("#!["))
|
||||
.map(|_| {
|
||||
attr.span()
|
||||
.with_lo(attr.span().lo() + BytePos(1))
|
||||
.with_hi(attr.span().lo() + BytePos(2))
|
||||
}),
|
||||
name: *attr_to_check,
|
||||
.map(|_| span.with_lo(span.lo() + BytePos(1)).with_hi(span.lo() + BytePos(2))),
|
||||
name,
|
||||
item,
|
||||
});
|
||||
|
||||
|
@ -2786,9 +2764,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
|
|||
err.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) {
|
||||
|
|
|
@ -582,13 +582,6 @@ pub(crate) struct NoMangle {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_repr_ident, code = E0565)]
|
||||
pub(crate) struct ReprIdent {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_repr_conflicting, code = E0566)]
|
||||
pub(crate) struct ReprConflicting {
|
||||
|
@ -736,31 +729,6 @@ pub(crate) struct Linkage {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_empty_confusables)]
|
||||
pub(crate) struct EmptyConfusables {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_incorrect_meta_item, code = E0539)]
|
||||
pub(crate) struct IncorrectMetaItem {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[subdiagnostic]
|
||||
pub suggestion: IncorrectMetaItemSuggestion,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(passes_incorrect_meta_item_suggestion, applicability = "maybe-incorrect")]
|
||||
pub(crate) struct IncorrectMetaItemSuggestion {
|
||||
#[suggestion_part(code = "\"")]
|
||||
pub lo: Span,
|
||||
#[suggestion_part(code = "\"")]
|
||||
pub hi: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_stability_promotable)]
|
||||
pub(crate) struct StabilityPromotable {
|
||||
|
@ -1475,14 +1443,6 @@ pub(crate) struct ObjectLifetimeErr {
|
|||
pub repr: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_unrecognized_repr_hint, code = E0552)]
|
||||
#[help]
|
||||
pub(crate) struct UnrecognizedReprHint {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
pub(crate) enum AttrApplication {
|
||||
#[diag(passes_attr_application_enum, code = E0517)]
|
||||
|
@ -1902,3 +1862,11 @@ pub(crate) struct NoSanitize<'a> {
|
|||
pub accepted_kind: &'a str,
|
||||
pub attr_str: &'a str,
|
||||
}
|
||||
|
||||
// FIXME(jdonszelmann): move back to rustc_attr
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_rustc_const_stable_indirect_pairing)]
|
||||
pub(crate) struct RustcConstStableIndirectPairing {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//! but are not declared in one single location (unlike lang features), which means we need to
|
||||
//! collect them instead.
|
||||
|
||||
use rustc_attr_parsing::VERSION_PLACEHOLDER;
|
||||
use rustc_attr_parsing::{AttributeKind, StabilityLevel, StableSince};
|
||||
use rustc_hir::Attribute;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
|
@ -26,66 +26,29 @@ impl<'tcx> LibFeatureCollector<'tcx> {
|
|||
}
|
||||
|
||||
fn extract(&self, attr: &Attribute) -> Option<(Symbol, FeatureStability, Span)> {
|
||||
let stab_attrs = [
|
||||
sym::stable,
|
||||
sym::unstable,
|
||||
sym::rustc_const_stable,
|
||||
sym::rustc_const_unstable,
|
||||
sym::rustc_default_body_unstable,
|
||||
];
|
||||
let (feature, level, span) = match attr {
|
||||
Attribute::Parsed(AttributeKind::Stability { stability, span }) => {
|
||||
(stability.feature, stability.level, *span)
|
||||
}
|
||||
Attribute::Parsed(AttributeKind::ConstStability { stability, span }) => {
|
||||
(stability.feature, stability.level, *span)
|
||||
}
|
||||
Attribute::Parsed(AttributeKind::BodyStability { stability, span }) => {
|
||||
(stability.feature, stability.level, *span)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
// Find a stability attribute: one of #[stable(…)], #[unstable(…)],
|
||||
// #[rustc_const_stable(…)], #[rustc_const_unstable(…)] or #[rustc_default_body_unstable].
|
||||
if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| attr.has_name(**stab_attr)) {
|
||||
if let Some(metas) = attr.meta_item_list() {
|
||||
let mut feature = None;
|
||||
let mut since = None;
|
||||
for meta in metas {
|
||||
if let Some(mi) = meta.meta_item() {
|
||||
// Find the `feature = ".."` meta-item.
|
||||
match (mi.name_or_empty(), mi.value_str()) {
|
||||
(sym::feature, val) => feature = val,
|
||||
(sym::since, val) => since = val,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
let feature_stability = match level {
|
||||
StabilityLevel::Unstable { .. } => FeatureStability::Unstable,
|
||||
StabilityLevel::Stable { since, .. } => FeatureStability::AcceptedSince(match since {
|
||||
StableSince::Version(v) => Symbol::intern(&v.to_string()),
|
||||
StableSince::Current => sym::env_CFG_RELEASE,
|
||||
StableSince::Err => return None,
|
||||
}),
|
||||
};
|
||||
|
||||
if let Some(s) = since
|
||||
&& s.as_str() == VERSION_PLACEHOLDER
|
||||
{
|
||||
since = Some(sym::env_CFG_RELEASE);
|
||||
}
|
||||
|
||||
if let Some(feature) = feature {
|
||||
// This additional check for stability is to make sure we
|
||||
// don't emit additional, irrelevant errors for malformed
|
||||
// attributes.
|
||||
let is_unstable = matches!(
|
||||
*stab_attr,
|
||||
sym::unstable
|
||||
| sym::rustc_const_unstable
|
||||
| sym::rustc_default_body_unstable
|
||||
);
|
||||
if is_unstable {
|
||||
return Some((feature, FeatureStability::Unstable, attr.span()));
|
||||
}
|
||||
if let Some(since) = since {
|
||||
return Some((
|
||||
feature,
|
||||
FeatureStability::AcceptedSince(since),
|
||||
attr.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
// We need to iterate over the other attributes, because
|
||||
// `rustc_const_unstable` is not mutually exclusive with
|
||||
// the other stability attributes, so we can't just `break`
|
||||
// here.
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
Some((feature, feature_stability, span))
|
||||
}
|
||||
|
||||
fn collect_feature(&mut self, feature: Symbol, stability: FeatureStability, span: Span) {
|
||||
|
|
|
@ -6,8 +6,8 @@ use std::num::NonZero;
|
|||
|
||||
use rustc_ast_lowering::stability::extern_abi_stability;
|
||||
use rustc_attr_parsing::{
|
||||
self as attr, ConstStability, DeprecatedSince, Stability, StabilityLevel, StableSince,
|
||||
UnstableReason, VERSION_PLACEHOLDER,
|
||||
self as attr, AttributeKind, ConstStability, DeprecatedSince, PartialConstStability, Stability,
|
||||
StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER, find_attr,
|
||||
};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
|
||||
|
@ -121,7 +121,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
|
|||
let attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id));
|
||||
debug!("annotate(id = {:?}, attrs = {:?})", def_id, attrs);
|
||||
|
||||
let depr = attr::find_deprecation(self.tcx.sess, self.tcx.features(), attrs);
|
||||
let depr = attr::find_attr!(attrs, AttributeKind::Deprecation{deprecation, span} => (*deprecation, *span));
|
||||
let const_stability_indirect = find_attr!(attrs, AttributeKind::ConstStabilityIndirect);
|
||||
|
||||
let mut is_deprecated = false;
|
||||
if let Some((depr, span)) = &depr {
|
||||
is_deprecated = true;
|
||||
|
@ -154,9 +156,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
|
|||
if inherit_deprecation.yes() && stab.is_unstable() {
|
||||
self.index.stab_map.insert(def_id, stab);
|
||||
if fn_sig.is_some_and(|s| s.header.is_const()) {
|
||||
let const_stab =
|
||||
attr::unmarked_crate_const_stab(self.tcx.sess, attrs, stab);
|
||||
self.index.const_stab_map.insert(def_id, const_stab);
|
||||
self.index.const_stab_map.insert(
|
||||
def_id,
|
||||
ConstStability::unmarked(const_stability_indirect, stab),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -171,9 +174,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
|
|||
}
|
||||
|
||||
// # Regular and body stability
|
||||
|
||||
let stab = attr::find_stability(self.tcx.sess, attrs, item_sp);
|
||||
let body_stab = attr::find_body_stability(self.tcx.sess, attrs);
|
||||
let stab = attr::find_attr!(attrs, AttributeKind::Stability { stability, span } => (*stability, *span));
|
||||
let body_stab =
|
||||
attr::find_attr!(attrs, AttributeKind::BodyStability { stability, .. } => *stability);
|
||||
|
||||
if let Some((depr, span)) = &depr
|
||||
&& depr.is_since_rustc_version()
|
||||
|
@ -182,7 +185,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
|
|||
self.tcx.dcx().emit_err(errors::DeprecatedAttribute { span: *span });
|
||||
}
|
||||
|
||||
if let Some((body_stab, _span)) = body_stab {
|
||||
if let Some(body_stab) = body_stab {
|
||||
// FIXME: check that this item can have body stability
|
||||
|
||||
self.index.default_body_stab_map.insert(def_id, body_stab);
|
||||
|
@ -260,10 +263,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
|
|||
|
||||
// # Const stability
|
||||
|
||||
let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item_sp);
|
||||
let const_stab = attr::find_attr!(attrs, AttributeKind::ConstStability { stability, span } => (*stability, *span));
|
||||
|
||||
// If the current node is a function with const stability attributes (directly given or
|
||||
// implied), check if the function/method is const.
|
||||
// implied), check if the function/method is const or the parent impl block is const.
|
||||
if let Some(fn_sig) = fn_sig
|
||||
&& !fn_sig.header.is_const()
|
||||
&& const_stab.is_some()
|
||||
|
@ -285,7 +288,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
|
|||
// Stable *language* features shouldn't be used as unstable library features.
|
||||
// (Not doing this for stable library features is checked by tidy.)
|
||||
if let Some((
|
||||
ConstStability { level: StabilityLevel::Unstable { .. }, feature, .. },
|
||||
PartialConstStability { level: StabilityLevel::Unstable { .. }, feature, .. },
|
||||
const_span,
|
||||
)) = const_stab
|
||||
{
|
||||
|
@ -297,9 +300,17 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some((stab, span)) = &const_stab
|
||||
&& stab.is_const_stable()
|
||||
&& const_stability_indirect
|
||||
{
|
||||
self.tcx.dcx().emit_err(errors::RustcConstStableIndirectPairing { span: *span });
|
||||
}
|
||||
|
||||
// After checking the immediate attributes, get rid of the span and compute implied
|
||||
// const stability: inherit feature gate from regular stability.
|
||||
let mut const_stab = const_stab.map(|(stab, _span)| stab);
|
||||
let mut const_stab = const_stab
|
||||
.map(|(stab, _span)| ConstStability::from_partial(stab, const_stability_indirect));
|
||||
|
||||
// If this is a const fn but not annotated with stability markers, see if we can inherit regular stability.
|
||||
if fn_sig.is_some_and(|s| s.header.is_const()) && const_stab.is_none() &&
|
||||
|
@ -785,8 +796,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
|
|||
let features = self.tcx.features();
|
||||
if features.staged_api() {
|
||||
let attrs = self.tcx.hir().attrs(item.hir_id());
|
||||
let stab = attr::find_stability(self.tcx.sess, attrs, item.span);
|
||||
let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item.span);
|
||||
let stab = attr::find_attr!(attrs, AttributeKind::Stability{stability, span} => (*stability, *span));
|
||||
|
||||
// FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem
|
||||
let const_stab = attr::find_attr!(attrs, AttributeKind::ConstStability{stability, ..} => *stability);
|
||||
|
||||
// If this impl block has an #[unstable] attribute, give an
|
||||
// error if all involved types and traits are stable, because
|
||||
|
@ -817,7 +830,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
|
|||
// needs to have an error emitted.
|
||||
if features.const_trait_impl()
|
||||
&& self.tcx.is_const_trait_impl(item.owner_id.to_def_id())
|
||||
&& const_stab.is_some_and(|(stab, _)| stab.is_const_stable())
|
||||
&& const_stab.is_some_and(|stab| stab.is_const_stable())
|
||||
{
|
||||
self.tcx.dcx().emit_err(errors::TraitImplConstStable { span: item.span });
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ use errors::{
|
|||
};
|
||||
use rustc_ast::MacroDef;
|
||||
use rustc_ast::visit::{VisitorResult, try_visit};
|
||||
use rustc_attr_parsing::AttributeKind;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_errors::{MultiSpan, listify};
|
||||
|
@ -493,7 +494,11 @@ impl<'tcx> EmbargoVisitor<'tcx> {
|
|||
// Non-opaque macros cannot make other items more accessible than they already are.
|
||||
let hir_id = self.tcx.local_def_id_to_hir_id(local_def_id);
|
||||
let attrs = self.tcx.hir().attrs(hir_id);
|
||||
if attr::find_transparency(attrs, md.macro_rules).0 != Transparency::Opaque {
|
||||
|
||||
if attr::find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x)
|
||||
.unwrap_or(Transparency::fallback(md.macro_rules))
|
||||
!= Transparency::Opaque
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::mem;
|
|||
use rustc_ast::visit::FnKind;
|
||||
use rustc_ast::*;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr_parsing::{AttributeParser, OmitDoc};
|
||||
use rustc_expand::expand::AstFragment;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind};
|
||||
|
@ -132,8 +133,19 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
|
|||
ItemKind::Fn(..) | ItemKind::Delegation(..) => DefKind::Fn,
|
||||
ItemKind::MacroDef(def) => {
|
||||
let edition = i.span.edition();
|
||||
|
||||
// FIXME(jdonszelmann) make one of these in the resolver?
|
||||
// FIXME(jdonszelmann) don't care about tools here maybe? Just parse what we can.
|
||||
// Does that prevents errors from happening? maybe
|
||||
let parser = AttributeParser::new(
|
||||
&self.resolver.tcx.sess,
|
||||
self.resolver.tcx.features(),
|
||||
Vec::new(),
|
||||
);
|
||||
let attrs = parser.parse_attribute_list(&i.attrs, i.span, OmitDoc::Skip);
|
||||
|
||||
let macro_data =
|
||||
self.resolver.compile_macro(def, i.ident, &i.attrs, i.span, i.id, edition);
|
||||
self.resolver.compile_macro(def, i.ident, &attrs, i.span, i.id, edition);
|
||||
let macro_kind = macro_data.ext.macro_kind();
|
||||
opt_macro_data = Some(macro_data);
|
||||
DefKind::Macro(macro_kind)
|
||||
|
|
|
@ -5,7 +5,6 @@ use std::cell::Cell;
|
|||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_ast::expand::StrippedCfgItem;
|
||||
use rustc_ast::{self as ast, Crate, NodeId, attr};
|
||||
use rustc_ast_pretty::pprust;
|
||||
|
@ -1112,7 +1111,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
&mut self,
|
||||
macro_def: &ast::MacroDef,
|
||||
ident: Ident,
|
||||
attrs: &[impl AttributeExt],
|
||||
attrs: &[rustc_hir::Attribute],
|
||||
span: Span,
|
||||
node_id: NodeId,
|
||||
edition: Edition,
|
||||
|
|
|
@ -8,6 +8,7 @@ bitflags = "2.5.0"
|
|||
tracing = "0.1"
|
||||
twox-hash = "1.6.3"
|
||||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
|
|
|
@ -175,6 +175,12 @@ pub enum Transparency {
|
|||
Opaque,
|
||||
}
|
||||
|
||||
impl Transparency {
|
||||
pub fn fallback(macro_rules: bool) -> Self {
|
||||
if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque }
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalExpnId {
|
||||
/// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST.
|
||||
pub const ROOT: LocalExpnId = LocalExpnId::ZERO;
|
||||
|
|
|
@ -9,6 +9,7 @@ punycode = "0.4.0"
|
|||
rustc-demangle = "0.1.21"
|
||||
|
||||
rustc_abi = { path = "../rustc_abi" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_hashes = { path = "../rustc_hashes" }
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
fn requires_alignment() {}
|
||||
|
||||
trait MyTrait {
|
||||
#[repr(align)] //~ ERROR `repr(align)` attributes on functions are unstable
|
||||
#[repr(align)] //~ ERROR invalid `repr(align)` attribute: `align` needs an argument
|
||||
fn myfun();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
error[E0589]: invalid `repr(align)` attribute: `align` needs an argument
|
||||
--> $DIR/feature-gate-fn_align.rs:7:12
|
||||
|
|
||||
LL | #[repr(align)]
|
||||
| ^^^^^ help: supply an argument here: `align(...)`
|
||||
|
||||
error[E0658]: `repr(align)` attributes on functions are unstable
|
||||
--> $DIR/feature-gate-fn_align.rs:3:8
|
||||
|
|
||||
|
@ -8,16 +14,7 @@ LL | #[repr(align(16))]
|
|||
= help: add `#![feature(fn_align)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: `repr(align)` attributes on functions are unstable
|
||||
--> $DIR/feature-gate-fn_align.rs:7:12
|
||||
|
|
||||
LL | #[repr(align)]
|
||||
| ^^^^^
|
||||
|
|
||||
= note: see issue #82232 <https://github.com/rust-lang/rust/issues/82232> for more information
|
||||
= help: add `#![feature(fn_align)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
||||
Some errors have detailed explanations: E0589, E0658.
|
||||
For more information about an error, try `rustc --explain E0589`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue