diff options
Diffstat (limited to 'compiler/rustc_attr_parsing/src')
20 files changed, 992 insertions, 306 deletions
diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index 1c51c3eee4e..a6bd2306ec5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -27,6 +27,26 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser { } } +pub(crate) struct UnstableFeatureBoundParser; +impl<S: Stage> CombineAttributeParser<S> for UnstableFeatureBoundParser { + const PATH: &'static [rustc_span::Symbol] = &[sym::unstable_feature_bound]; + type Item = (Symbol, Span); + const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::UnstableFeatureBound(items); + const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ..."); + + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator<Item = Self::Item> { + if !cx.features().staged_api() { + cx.emit_err(session_diagnostics::StabilityOutsideStd { span: cx.attr_span }); + } + parse_unstable(cx, args, <Self as CombineAttributeParser<S>>::PATH[0]) + .into_iter() + .zip(iter::repeat(cx.attr_span)) + } +} + pub(crate) struct AllowConstFnUnstableParser; impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser { const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable]; diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index a8d9229cbc3..6373cf6e08a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -1,247 +1,298 @@ -use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId}; -use rustc_ast_pretty::pprust; -use rustc_attr_data_structures::RustcVersion; -use rustc_feature::{Features, GatedCfg, find_gated_cfg}; +use rustc_ast::{LitKind, NodeId}; +use rustc_attr_data_structures::{CfgEntry, RustcVersion}; +use rustc_feature::{AttributeTemplate, Features, template}; use rustc_session::Session; use rustc_session::config::ExpectedValues; +use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::UNEXPECTED_CFGS; -use rustc_session::lint::{BuiltinLintDiag, Lint}; use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; +use thin_vec::ThinVec; -use crate::session_diagnostics::{self, UnsupportedLiteralReason}; -use crate::{fluent_generated, parse_version}; +use crate::context::{AcceptContext, ShouldEmit, Stage}; +use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser}; +use crate::{ + CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics, try_gate_cfg, +}; -/// Emitter of a builtin lint from `cfg_matches`. -/// -/// Used to support emitting a lint (currently on check-cfg), either: -/// - as an early buffered lint (in `rustc`) -/// - or has a "normal" lint from HIR (in `rustdoc`) -pub trait CfgMatchesLintEmitter { - fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag); -} - -impl CfgMatchesLintEmitter for NodeId { - fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag) { - sess.psess.buffer_lint(lint, sp, *self, diag); - } -} +pub const CFG_TEMPLATE: AttributeTemplate = template!(List: "predicate"); -#[derive(Clone, Debug)] -pub struct Condition { - pub name: Symbol, - pub name_span: Span, - pub value: Option<Symbol>, - pub value_span: Option<Span>, - pub span: Span, +pub fn parse_cfg_attr<'c, S: Stage>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, +) -> Option<CfgEntry> { + let ArgParser::List(list) = args else { + cx.expected_list(cx.attr_span); + return None; + }; + let Some(single) = list.single() else { + cx.expected_single_argument(list.span); + return None; + }; + parse_cfg_entry(cx, single) } -/// Tests if a cfg-pattern matches the cfg set -pub fn cfg_matches( - cfg: &MetaItemInner, - sess: &Session, - lint_emitter: impl CfgMatchesLintEmitter, - features: Option<&Features>, -) -> bool { - eval_condition(cfg, sess, features, &mut |cfg| { - try_gate_cfg(cfg.name, cfg.span, sess, features); - match sess.psess.check_config.expecteds.get(&cfg.name) { - Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => { - lint_emitter.emit_span_lint( - sess, - UNEXPECTED_CFGS, - cfg.span, - BuiltinLintDiag::UnexpectedCfgValue( - (cfg.name, cfg.name_span), - cfg.value.map(|v| (v, cfg.value_span.unwrap())), - ), - ); +fn parse_cfg_entry<S: Stage>( + cx: &mut AcceptContext<'_, '_, S>, + item: &MetaItemOrLitParser<'_>, +) -> Option<CfgEntry> { + Some(match item { + MetaItemOrLitParser::MetaItemParser(meta) => match meta.args() { + ArgParser::List(list) => match meta.path().word_sym() { + Some(sym::not) => { + let Some(single) = list.single() else { + cx.expected_single_argument(list.span); + return None; + }; + CfgEntry::Not(Box::new(parse_cfg_entry(cx, single)?), list.span) + } + Some(sym::any) => CfgEntry::Any( + list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(), + list.span, + ), + Some(sym::all) => CfgEntry::All( + list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(), + list.span, + ), + Some(sym::target) => parse_cfg_entry_target(cx, list, meta.span())?, + Some(sym::version) => parse_cfg_entry_version(cx, list, meta.span())?, + _ => { + cx.emit_err(session_diagnostics::InvalidPredicate { + span: meta.span(), + predicate: meta.path().to_string(), + }); + return None; + } + }, + a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => { + let Some(name) = meta.path().word_sym() else { + cx.emit_err(session_diagnostics::CfgPredicateIdentifier { + span: meta.path().span(), + }); + return None; + }; + parse_name_value(name, meta.path().span(), a.name_value(), meta.span(), cx)? } - None if sess.psess.check_config.exhaustive_names => { - lint_emitter.emit_span_lint( - sess, - UNEXPECTED_CFGS, - cfg.span, - BuiltinLintDiag::UnexpectedCfgName( - (cfg.name, cfg.name_span), - cfg.value.map(|v| (v, cfg.value_span.unwrap())), - ), - ); + }, + MetaItemOrLitParser::Lit(lit) => match lit.kind { + LitKind::Bool(b) => CfgEntry::Bool(b, lit.span), + _ => { + cx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: lit.span }); + return None; } - _ => { /* not unexpected */ } - } - sess.psess.config.contains(&(cfg.name, cfg.value)) + }, + MetaItemOrLitParser::Err(_, _) => return None, }) } -fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) { - let gate = find_gated_cfg(|sym| sym == name); - if let (Some(feats), Some(gated_cfg)) = (features, gate) { - gate_cfg(gated_cfg, span, sess, feats); - } +fn parse_cfg_entry_version<S: Stage>( + cx: &mut AcceptContext<'_, '_, S>, + list: &MetaItemListParser<'_>, + meta_span: Span, +) -> Option<CfgEntry> { + try_gate_cfg(sym::version, meta_span, cx.sess(), cx.features_option()); + let Some(version) = list.single() else { + cx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { span: list.span }); + return None; + }; + let Some(version_lit) = version.lit() else { + cx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: version.span() }); + return None; + }; + let Some(version_str) = version_lit.value_str() else { + cx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: version_lit.span }); + return None; + }; + + let min_version = parse_version(version_str).or_else(|| { + cx.sess() + .dcx() + .emit_warn(session_diagnostics::UnknownVersionLiteral { span: version_lit.span }); + None + }); + + Some(CfgEntry::Version(min_version, list.span)) } -#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable -fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) { - let (cfg, feature, has_feature) = gated_cfg; - if !has_feature(features) && !cfg_span.allows_unstable(*feature) { - let explain = format!("`cfg({cfg})` is experimental and subject to change"); - feature_err(sess, *feature, cfg_span, explain).emit(); +fn parse_cfg_entry_target<S: Stage>( + cx: &mut AcceptContext<'_, '_, S>, + list: &MetaItemListParser<'_>, + meta_span: Span, +) -> Option<CfgEntry> { + if let Some(features) = cx.features_option() + && !features.cfg_target_compact() + { + feature_err( + cx.sess(), + sym::cfg_target_compact, + meta_span, + fluent_generated::attr_parsing_unstable_cfg_target_compact, + ) + .emit(); } -} -/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to -/// evaluate individual items. -pub fn eval_condition( - cfg: &MetaItemInner, - sess: &Session, - features: Option<&Features>, - eval: &mut impl FnMut(Condition) -> bool, -) -> bool { - let dcx = sess.dcx(); - - let cfg = match cfg { - MetaItemInner::MetaItem(meta_item) => meta_item, - MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => { - return *b; - } - _ => { - dcx.emit_err(session_diagnostics::UnsupportedLiteral { - span: cfg.span(), - reason: UnsupportedLiteralReason::CfgBoolean, - is_bytestr: false, - start_point_span: sess.source_map().start_point(cfg.span()), + let mut result = ThinVec::new(); + for sub_item in list.mixed() { + // First, validate that this is a NameValue item + let Some(sub_item) = sub_item.meta_item() else { + cx.expected_name_value(sub_item.span(), None); + continue; + }; + let Some(nv) = sub_item.args().name_value() else { + cx.expected_name_value(sub_item.span(), None); + continue; + }; + + // Then, parse it as a name-value item + let Some(name) = sub_item.path().word_sym() else { + cx.emit_err(session_diagnostics::CfgPredicateIdentifier { + span: sub_item.path().span(), }); - return false; + return None; + }; + let name = Symbol::intern(&format!("target_{name}")); + if let Some(cfg) = + parse_name_value(name, sub_item.path().span(), Some(nv), sub_item.span(), cx) + { + result.push(cfg); + } + } + Some(CfgEntry::All(result, list.span)) +} + +fn parse_name_value<S: Stage>( + name: Symbol, + name_span: Span, + value: Option<&NameValueParser>, + span: Span, + cx: &mut AcceptContext<'_, '_, S>, +) -> Option<CfgEntry> { + try_gate_cfg(name, span, cx.sess(), cx.features_option()); + + let value = match value { + None => None, + Some(value) => { + let Some(value_str) = value.value_as_str() else { + cx.expected_string_literal(value.value_span, Some(value.value_as_lit())); + return None; + }; + Some((value_str, value.value_span)) } }; - match &cfg.kind { - MetaItemKind::List(mis) if cfg.has_name(sym::version) => { - try_gate_cfg(sym::version, cfg.span, sess, features); - let (min_version, span) = match &mis[..] { - [MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => { - (sym, span) - } - [ - MetaItemInner::Lit(MetaItemLit { span, .. }) - | MetaItemInner::MetaItem(MetaItem { span, .. }), - ] => { - dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span }); - return false; + Some(CfgEntry::NameValue { name, name_span, value, span }) +} + +pub fn eval_config_entry( + sess: &Session, + cfg_entry: &CfgEntry, + id: NodeId, + features: Option<&Features>, + emit_lints: ShouldEmit, +) -> EvalConfigResult { + match cfg_entry { + CfgEntry::All(subs, ..) => { + let mut all = None; + for sub in subs { + let res = eval_config_entry(sess, sub, id, features, emit_lints); + // We cannot short-circuit because `eval_config_entry` emits some lints + if !res.as_bool() { + all.get_or_insert(res); } - [..] => { - dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { - span: cfg.span, - }); - return false; + } + all.unwrap_or_else(|| EvalConfigResult::True) + } + CfgEntry::Any(subs, span) => { + let mut any = None; + for sub in subs { + let res = eval_config_entry(sess, sub, id, features, emit_lints); + // We cannot short-circuit because `eval_config_entry` emits some lints + if res.as_bool() { + any.get_or_insert(res); } - }; - let Some(min_version) = parse_version(*min_version) else { - dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span }); - return false; - }; - - // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details - if sess.psess.assume_incomplete_release { - RustcVersion::current_overridable() > min_version + } + any.unwrap_or_else(|| EvalConfigResult::False { + reason: cfg_entry.clone(), + reason_span: *span, + }) + } + CfgEntry::Not(sub, span) => { + if eval_config_entry(sess, sub, id, features, emit_lints).as_bool() { + EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } } else { - RustcVersion::current_overridable() >= min_version + EvalConfigResult::True } } - MetaItemKind::List(mis) => { - for mi in mis.iter() { - if mi.meta_item_or_bool().is_none() { - dcx.emit_err(session_diagnostics::UnsupportedLiteral { - span: mi.span(), - reason: UnsupportedLiteralReason::Generic, - is_bytestr: false, - start_point_span: sess.source_map().start_point(mi.span()), - }); - return false; - } + CfgEntry::Bool(b, span) => { + if *b { + EvalConfigResult::True + } else { + EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } } - - // The unwraps below may look dangerous, but we've already asserted - // that they won't fail with the loop above. - match cfg.name() { - Some(sym::any) => mis - .iter() - // We don't use any() here, because we want to evaluate all cfg condition - // as eval_condition can (and does) extra checks - .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)), - Some(sym::all) => mis - .iter() - // We don't use all() here, because we want to evaluate all cfg condition - // as eval_condition can (and does) extra checks - .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)), - Some(sym::not) => { - let [mi] = mis.as_slice() else { - dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span }); - return false; - }; - - !eval_condition(mi, sess, features, eval) - } - Some(sym::target) => { - if let Some(features) = features - && !features.cfg_target_compact() + } + CfgEntry::NameValue { name, name_span, value, span } => { + if let ShouldEmit::ErrorsAndLints = emit_lints { + match sess.psess.check_config.expecteds.get(name) { + Some(ExpectedValues::Some(values)) + if !values.contains(&value.map(|(v, _)| v)) => { - feature_err( + id.emit_span_lint( sess, - sym::cfg_target_compact, - cfg.span, - fluent_generated::attr_parsing_unstable_cfg_target_compact, - ) - .emit(); + UNEXPECTED_CFGS, + *span, + BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value), + ); } - - mis.iter().fold(true, |res, mi| { - let Some(mut mi) = mi.meta_item().cloned() else { - dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { - span: mi.span(), - }); - return false; - }; - - if let [seg, ..] = &mut mi.path.segments[..] { - seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name)); - } - - res & eval_condition(&MetaItemInner::MetaItem(mi), sess, features, eval) - }) - } - _ => { - dcx.emit_err(session_diagnostics::InvalidPredicate { - span: cfg.span, - predicate: pprust::path_to_string(&cfg.path), - }); - false + None if sess.psess.check_config.exhaustive_names => { + id.emit_span_lint( + sess, + UNEXPECTED_CFGS, + *span, + BuiltinLintDiag::UnexpectedCfgName((*name, *name_span), *value), + ); + } + _ => { /* not unexpected */ } } } + + if sess.psess.config.contains(&(*name, value.map(|(v, _)| v))) { + EvalConfigResult::True + } else { + EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } + } } - MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => { - dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span }); - true - } - MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { - dcx.emit_err(session_diagnostics::UnsupportedLiteral { - span: lit.span, - reason: UnsupportedLiteralReason::CfgString, - is_bytestr: lit.kind.is_bytestr(), - start_point_span: sess.source_map().start_point(lit.span), - }); - true + CfgEntry::Version(min_version, version_span) => { + let Some(min_version) = min_version else { + return EvalConfigResult::False { + reason: cfg_entry.clone(), + reason_span: *version_span, + }; + }; + // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details + let min_version_ok = if sess.psess.assume_incomplete_release { + RustcVersion::current_overridable() > *min_version + } else { + RustcVersion::current_overridable() >= *min_version + }; + if min_version_ok { + EvalConfigResult::True + } else { + EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *version_span } + } } - MetaItemKind::Word | MetaItemKind::NameValue(..) => { - let ident = cfg.ident().expect("multi-segment cfg predicate"); - eval(Condition { - name: ident.name, - name_span: ident.span, - value: cfg.value_str(), - value_span: cfg.name_value_literal_span(), - span: cfg.span, - }) + } +} + +pub enum EvalConfigResult { + True, + False { reason: CfgEntry, reason_span: Span }, +} + +impl EvalConfigResult { + pub fn as_bool(&self) -> bool { + match self { + EvalConfigResult::True => true, + EvalConfigResult::False { .. } => false, } } } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_old.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_old.rs new file mode 100644 index 00000000000..c5025a8b6ea --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_old.rs @@ -0,0 +1,247 @@ +use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId}; +use rustc_ast_pretty::pprust; +use rustc_attr_data_structures::RustcVersion; +use rustc_feature::{Features, GatedCfg, find_gated_cfg}; +use rustc_session::Session; +use rustc_session::config::ExpectedValues; +use rustc_session::lint::builtin::UNEXPECTED_CFGS; +use rustc_session::lint::{BuiltinLintDiag, Lint}; +use rustc_session::parse::feature_err; +use rustc_span::{Span, Symbol, sym}; + +use crate::session_diagnostics::{self, UnsupportedLiteralReason}; +use crate::{fluent_generated, parse_version}; + +/// Emitter of a builtin lint from `cfg_matches`. +/// +/// Used to support emitting a lint (currently on check-cfg), either: +/// - as an early buffered lint (in `rustc`) +/// - or has a "normal" lint from HIR (in `rustdoc`) +pub trait CfgMatchesLintEmitter { + fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag); +} + +impl CfgMatchesLintEmitter for NodeId { + fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag) { + sess.psess.buffer_lint(lint, sp, *self, diag); + } +} + +#[derive(Clone, Debug)] +pub struct Condition { + pub name: Symbol, + pub name_span: Span, + pub value: Option<Symbol>, + pub value_span: Option<Span>, + pub span: Span, +} + +/// Tests if a cfg-pattern matches the cfg set +pub fn cfg_matches( + cfg: &MetaItemInner, + sess: &Session, + lint_emitter: impl CfgMatchesLintEmitter, + features: Option<&Features>, +) -> bool { + eval_condition(cfg, sess, features, &mut |cfg| { + try_gate_cfg(cfg.name, cfg.span, sess, features); + match sess.psess.check_config.expecteds.get(&cfg.name) { + Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => { + lint_emitter.emit_span_lint( + sess, + UNEXPECTED_CFGS, + cfg.span, + BuiltinLintDiag::UnexpectedCfgValue( + (cfg.name, cfg.name_span), + cfg.value.map(|v| (v, cfg.value_span.unwrap())), + ), + ); + } + None if sess.psess.check_config.exhaustive_names => { + lint_emitter.emit_span_lint( + sess, + UNEXPECTED_CFGS, + cfg.span, + BuiltinLintDiag::UnexpectedCfgName( + (cfg.name, cfg.name_span), + cfg.value.map(|v| (v, cfg.value_span.unwrap())), + ), + ); + } + _ => { /* not unexpected */ } + } + sess.psess.config.contains(&(cfg.name, cfg.value)) + }) +} + +pub fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) { + let gate = find_gated_cfg(|sym| sym == name); + if let (Some(feats), Some(gated_cfg)) = (features, gate) { + gate_cfg(gated_cfg, span, sess, feats); + } +} + +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable +fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) { + let (cfg, feature, has_feature) = gated_cfg; + if !has_feature(features) && !cfg_span.allows_unstable(*feature) { + let explain = format!("`cfg({cfg})` is experimental and subject to change"); + feature_err(sess, *feature, cfg_span, explain).emit(); + } +} + +/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to +/// evaluate individual items. +pub fn eval_condition( + cfg: &MetaItemInner, + sess: &Session, + features: Option<&Features>, + eval: &mut impl FnMut(Condition) -> bool, +) -> bool { + let dcx = sess.dcx(); + + let cfg = match cfg { + MetaItemInner::MetaItem(meta_item) => meta_item, + MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => { + return *b; + } + _ => { + dcx.emit_err(session_diagnostics::UnsupportedLiteral { + span: cfg.span(), + reason: UnsupportedLiteralReason::CfgBoolean, + is_bytestr: false, + start_point_span: sess.source_map().start_point(cfg.span()), + }); + return false; + } + }; + + match &cfg.kind { + MetaItemKind::List(mis) if cfg.has_name(sym::version) => { + try_gate_cfg(sym::version, cfg.span, sess, features); + let (min_version, span) = match &mis[..] { + [MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => { + (sym, span) + } + [ + MetaItemInner::Lit(MetaItemLit { span, .. }) + | MetaItemInner::MetaItem(MetaItem { span, .. }), + ] => { + dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span }); + return false; + } + [..] => { + dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { + span: cfg.span, + }); + return false; + } + }; + let Some(min_version) = parse_version(*min_version) else { + dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span }); + return false; + }; + + // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details + if sess.psess.assume_incomplete_release { + RustcVersion::current_overridable() > min_version + } else { + RustcVersion::current_overridable() >= min_version + } + } + MetaItemKind::List(mis) => { + for mi in mis.iter() { + if mi.meta_item_or_bool().is_none() { + dcx.emit_err(session_diagnostics::UnsupportedLiteral { + span: mi.span(), + reason: UnsupportedLiteralReason::Generic, + is_bytestr: false, + start_point_span: sess.source_map().start_point(mi.span()), + }); + return false; + } + } + + // The unwraps below may look dangerous, but we've already asserted + // that they won't fail with the loop above. + match cfg.name() { + Some(sym::any) => mis + .iter() + // We don't use any() here, because we want to evaluate all cfg condition + // as eval_condition can (and does) extra checks + .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)), + Some(sym::all) => mis + .iter() + // We don't use all() here, because we want to evaluate all cfg condition + // as eval_condition can (and does) extra checks + .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)), + Some(sym::not) => { + let [mi] = mis.as_slice() else { + dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span }); + return false; + }; + + !eval_condition(mi, sess, features, eval) + } + Some(sym::target) => { + if let Some(features) = features + && !features.cfg_target_compact() + { + feature_err( + sess, + sym::cfg_target_compact, + cfg.span, + fluent_generated::attr_parsing_unstable_cfg_target_compact, + ) + .emit(); + } + + mis.iter().fold(true, |res, mi| { + let Some(mut mi) = mi.meta_item().cloned() else { + dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { + span: mi.span(), + }); + return false; + }; + + if let [seg, ..] = &mut mi.path.segments[..] { + seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name)); + } + + res & eval_condition(&MetaItemInner::MetaItem(mi), sess, features, eval) + }) + } + _ => { + dcx.emit_err(session_diagnostics::InvalidPredicate { + span: cfg.span, + predicate: pprust::path_to_string(&cfg.path), + }); + false + } + } + } + MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => { + dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span }); + true + } + MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { + dcx.emit_err(session_diagnostics::UnsupportedLiteral { + span: lit.span, + reason: UnsupportedLiteralReason::CfgString, + is_bytestr: lit.kind.is_bytestr(), + start_point_span: sess.source_map().start_point(lit.span), + }); + true + } + MetaItemKind::Word | MetaItemKind::NameValue(..) => { + let ident = cfg.ident().expect("multi-segment cfg predicate"); + eval(Condition { + name: ident.name, + name_span: ident.span, + value: cfg.value_str(), + value_span: cfg.name_value_literal_span(), + span: cfg.span, + }) + } + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index cb3956d46a0..34d9b048348 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -15,7 +15,7 @@ pub(crate) struct OptimizeParser; impl<S: Stage> SingleAttributeParser<S> for OptimizeParser { const PATH: &[Symbol] = &[sym::optimize]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none"); @@ -56,7 +56,7 @@ pub(crate) struct ExportNameParser; impl<S: Stage> SingleAttributeParser<S> for ExportNameParser { const PATH: &[rustc_span::Symbol] = &[sym::export_name]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); @@ -334,3 +334,11 @@ impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser { features } } + +pub(crate) struct OmitGdbPrettyPrinterSectionParser; + +impl<S: Stage> NoArgsAttributeParser<S> for OmitGdbPrettyPrinterSectionParser { + const PATH: &[Symbol] = &[sym::omit_gdb_pretty_printer_section]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::OmitGdbPrettyPrinterSection; +} diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 702ad66f578..08cf1ab5d19 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -36,7 +36,7 @@ fn get<S: Stage>( impl<S: Stage> SingleAttributeParser<S> for DeprecationParser { const PATH: &[Symbol] = &[sym::deprecated]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!( Word, diff --git a/compiler/rustc_attr_parsing/src/attributes/dummy.rs b/compiler/rustc_attr_parsing/src/attributes/dummy.rs new file mode 100644 index 00000000000..e5e1c3bb6b6 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/dummy.rs @@ -0,0 +1,19 @@ +use rustc_attr_data_structures::AttributeKind; +use rustc_feature::{AttributeTemplate, template}; +use rustc_span::{Symbol, sym}; + +use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; + +pub(crate) struct DummyParser; +impl<S: Stage> SingleAttributeParser<S> for DummyParser { + const PATH: &[Symbol] = &[sym::rustc_dummy]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Ignore; + const TEMPLATE: AttributeTemplate = template!(Word); // Anything, really + + fn convert(_: &mut AcceptContext<'_, '_, S>, _: &ArgParser<'_>) -> Option<AttributeKind> { + Some(AttributeKind::Dummy) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index 11844f4cd95..fe812175218 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -16,7 +16,7 @@ pub(crate) struct InlineParser; impl<S: Stage> SingleAttributeParser<S> for InlineParser { const PATH: &'static [Symbol] = &[sym::inline]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(Word, List: "always|never"); @@ -57,7 +57,7 @@ pub(crate) struct RustcForceInlineParser; impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser { const PATH: &'static [Symbol] = &[sym::rustc_force_inline]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(Word, List: "reason", NameValueStr: "reason"); diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index e298053ab76..960cebd8925 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -1,18 +1,20 @@ use rustc_attr_data_structures::AttributeKind; -use rustc_attr_data_structures::AttributeKind::{LinkName, LinkSection}; +use rustc_attr_data_structures::AttributeKind::{LinkName, LinkOrdinal, LinkSection}; use rustc_feature::{AttributeTemplate, template}; -use rustc_span::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; -use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; -use crate::context::{AcceptContext, Stage}; +use crate::attributes::{ + AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser, +}; +use crate::context::{AcceptContext, Stage, parse_single_integer}; use crate::parser::ArgParser; -use crate::session_diagnostics::NullOnLinkSection; +use crate::session_diagnostics::{LinkOrdinalOutOfRange, NullOnLinkSection}; pub(crate) struct LinkNameParser; impl<S: Stage> SingleAttributeParser<S> for LinkNameParser { const PATH: &[Symbol] = &[sym::link_name]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); @@ -34,7 +36,7 @@ pub(crate) struct LinkSectionParser; impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser { const PATH: &[Symbol] = &[sym::link_section]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); @@ -57,3 +59,64 @@ impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser { Some(LinkSection { name, span: cx.attr_span }) } } + +pub(crate) struct ExportStableParser; +impl<S: Stage> NoArgsAttributeParser<S> for ExportStableParser { + const PATH: &[Symbol] = &[sym::export_stable]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn; + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ExportStable; +} + +pub(crate) struct FfiConstParser; +impl<S: Stage> NoArgsAttributeParser<S> for FfiConstParser { + const PATH: &[Symbol] = &[sym::ffi_const]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiConst; +} + +pub(crate) struct FfiPureParser; +impl<S: Stage> NoArgsAttributeParser<S> for FfiPureParser { + const PATH: &[Symbol] = &[sym::ffi_pure]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure; +} + +pub(crate) struct StdInternalSymbolParser; +impl<S: Stage> NoArgsAttributeParser<S> for StdInternalSymbolParser { + const PATH: &[Symbol] = &[sym::rustc_std_internal_symbol]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::StdInternalSymbol; +} + +pub(crate) struct LinkOrdinalParser; + +impl<S: Stage> SingleAttributeParser<S> for LinkOrdinalParser { + const PATH: &[Symbol] = &[sym::link_ordinal]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = template!(List: "ordinal"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + let ordinal = parse_single_integer(cx, args)?; + + // According to the table at + // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the + // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined + // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import + // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t. + // + // FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for + // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that + // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import + // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an + // import library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I + // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL -- + // see earlier comment about LINK.EXE failing.) + let Ok(ordinal) = ordinal.try_into() else { + cx.emit_err(LinkOrdinalOutOfRange { span: cx.attr_span, ordinal }); + return None; + }; + + Some(LinkOrdinal { ordinal, span: cx.attr_span }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs index 8ad98c8d1d4..0eceff53e8b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -24,3 +24,10 @@ impl<S: Stage> NoArgsAttributeParser<S> for PassByValueParser { const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; const CREATE: fn(Span) -> AttributeKind = AttributeKind::PassByValue; } + +pub(crate) struct AutomaticallyDerivedParser; +impl<S: Stage> NoArgsAttributeParser<S> for AutomaticallyDerivedParser { + const PATH: &[Symbol] = &[sym::automatically_derived]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::AutomaticallyDerived; +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index ba7572434df..200f1381960 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -27,9 +27,11 @@ use crate::session_diagnostics::UnusedMultiple; pub(crate) mod allow_unstable; pub(crate) mod cfg; +pub(crate) mod cfg_old; pub(crate) mod codegen_attrs; pub(crate) mod confusables; pub(crate) mod deprecation; +pub(crate) mod dummy; pub(crate) mod inline; pub(crate) mod link_attrs; pub(crate) mod lint_helpers; @@ -139,7 +141,7 @@ impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S> if let Some(pa) = T::convert(cx, args) { match T::ATTRIBUTE_ORDER { // keep the first and report immediately. ignore this attribute - AttributeOrder::KeepFirst => { + AttributeOrder::KeepInnermost => { if let Some((_, unused)) = group.1 { T::ON_DUPLICATE.exec::<T>(cx, cx.attr_span, unused); return; @@ -147,7 +149,7 @@ impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S> } // keep the new one and warn about the previous, // then replace - AttributeOrder::KeepLast => { + AttributeOrder::KeepOutermost => { if let Some((_, used)) = group.1 { T::ON_DUPLICATE.exec::<T>(cx, used, cx.attr_span); } @@ -164,9 +166,6 @@ impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S> } } -// FIXME(jdonszelmann): logic is implemented but the attribute parsers needing -// them will be merged in another PR -#[allow(unused)] pub(crate) enum OnDuplicate<S: Stage> { /// Give a default warning Warn, @@ -212,25 +211,29 @@ impl<S: Stage> OnDuplicate<S> { } } } -// -// FIXME(jdonszelmann): logic is implemented but the attribute parsers needing -// them will be merged in another PR -#[allow(unused)] + pub(crate) enum AttributeOrder { - /// Duplicates after the first attribute will be an error. + /// Duplicates after the innermost instance of the attribute will be an error/warning. + /// Only keep the lowest attribute. /// - /// This should be used where duplicates would be ignored, but carry extra - /// meaning that could cause confusion. For example, `#[stable(since="1.0")] - /// #[stable(since="2.0")]`, which version should be used for `stable`? - KeepFirst, - - /// Duplicates preceding the last instance of the attribute will be a - /// warning, with a note that this will be an error in the future. + /// Attributes are processed from bottom to top, so this raises a warning/error on all the attributes + /// further above the lowest one: + /// ``` + /// #[stable(since="1.0")] //~ WARNING duplicated attribute + /// #[stable(since="2.0")] + /// ``` + KeepInnermost, + + /// Duplicates before the outermost instance of the attribute will be an error/warning. + /// Only keep the highest attribute. /// - /// This is the same as `FutureWarnFollowing`, except the last attribute is - /// the one that is "used". Ideally these can eventually migrate to - /// `ErrorPreceding`. - KeepLast, + /// Attributes are processed from bottom to top, so this raises a warning/error on all the attributes + /// below the highest one: + /// ``` + /// #[path="foo.rs"] + /// #[path="bar.rs"] //~ WARNING duplicated attribute + /// ``` + KeepOutermost, } /// An even simpler version of [`SingleAttributeParser`]: @@ -256,7 +259,7 @@ impl<T: NoArgsAttributeParser<S>, S: Stage> Default for WithoutArgs<T, S> { impl<T: NoArgsAttributeParser<S>, S: Stage> SingleAttributeParser<S> for WithoutArgs<T, S> { const PATH: &[Symbol] = T::PATH; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate<S> = T::ON_DUPLICATE; const TEMPLATE: AttributeTemplate = template!(Word); diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs index b5eb85f68b4..e0a3e675509 100644 --- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs +++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs @@ -12,7 +12,7 @@ pub(crate) struct MustUseParser; impl<S: Stage> SingleAttributeParser<S> for MustUseParser { const PATH: &[Symbol] = &[sym::must_use]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(Word, NameValueStr: "reason"); diff --git a/compiler/rustc_attr_parsing/src/attributes/path.rs b/compiler/rustc_attr_parsing/src/attributes/path.rs index 0dfbc9a9aa8..febb1b45a18 100644 --- a/compiler/rustc_attr_parsing/src/attributes/path.rs +++ b/compiler/rustc_attr_parsing/src/attributes/path.rs @@ -10,7 +10,7 @@ pub(crate) struct PathParser; impl<S: Stage> SingleAttributeParser<S> for PathParser { const PATH: &[Symbol] = &[sym::path]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "file"); diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index e6b6a6fe3c9..7ca951dc0bb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -1,23 +1,22 @@ -use rustc_ast::LitKind; use rustc_attr_data_structures::AttributeKind; use rustc_feature::{AttributeTemplate, template}; use rustc_span::{Symbol, sym}; use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; -use crate::context::{AcceptContext, Stage}; +use crate::context::{AcceptContext, Stage, parse_single_integer}; use crate::parser::ArgParser; pub(crate) struct RustcLayoutScalarValidRangeStart; impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStart { const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_start]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(List: "start"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { - parse_rustc_layout_scalar_valid_range(cx, args) - .map(|n| AttributeKind::RustcLayoutScalarValidRangeStart(n, cx.attr_span)) + parse_single_integer(cx, args) + .map(|n| AttributeKind::RustcLayoutScalarValidRangeStart(Box::new(n), cx.attr_span)) } } @@ -25,44 +24,21 @@ pub(crate) struct RustcLayoutScalarValidRangeEnd; impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeEnd { const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_end]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(List: "end"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { - parse_rustc_layout_scalar_valid_range(cx, args) - .map(|n| AttributeKind::RustcLayoutScalarValidRangeEnd(n, cx.attr_span)) + parse_single_integer(cx, args) + .map(|n| AttributeKind::RustcLayoutScalarValidRangeEnd(Box::new(n), cx.attr_span)) } } -fn parse_rustc_layout_scalar_valid_range<S: Stage>( - cx: &mut AcceptContext<'_, '_, S>, - args: &ArgParser<'_>, -) -> Option<Box<u128>> { - let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); - return None; - }; - let Some(single) = list.single() else { - cx.expected_single_argument(list.span); - return None; - }; - let Some(lit) = single.lit() else { - cx.expected_integer_literal(single.span()); - return None; - }; - let LitKind::Int(num, _ty) = lit.kind else { - cx.expected_integer_literal(single.span()); - return None; - }; - Some(Box::new(num.0)) -} - pub(crate) struct RustcObjectLifetimeDefaultParser; impl<S: Stage> SingleAttributeParser<S> for RustcObjectLifetimeDefaultParser { const PATH: &[rustc_span::Symbol] = &[sym::rustc_object_lifetime_default]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(Word); diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 6bccd0042a8..8f405e5aad9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -98,6 +98,16 @@ impl<S: Stage> AttributeParser<S> for StabilityParser { } } + if let Some((Stability { level: StabilityLevel::Stable { .. }, .. }, _)) = self.stability { + for other_attr in cx.all_attrs { + if other_attr.word_is(sym::unstable_feature_bound) { + cx.emit_err(session_diagnostics::UnstableFeatureBoundIncompatibleStability { + span: cx.target_span, + }); + } + } + } + let (stability, span) = self.stability?; Some(AttributeKind::Stability { stability, span }) diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs index cea3ee52ff4..ee81f64860f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs @@ -11,7 +11,7 @@ pub(crate) struct IgnoreParser; impl<S: Stage> SingleAttributeParser<S> for IgnoreParser { const PATH: &[Symbol] = &[sym::ignore]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn; const TEMPLATE: AttributeTemplate = template!(Word, NameValueStr: "reason"); diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index 83a98c53c7f..e69a533699b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -2,17 +2,18 @@ use core::mem; use rustc_attr_data_structures::AttributeKind; use rustc_feature::{AttributeTemplate, template}; -use rustc_span::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; -use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::attributes::{ + AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser, +}; use crate::context::{AcceptContext, Stage}; use crate::parser::ArgParser; pub(crate) struct SkipDuringMethodDispatchParser; - impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser { const PATH: &[Symbol] = &[sym::rustc_skip_during_method_dispatch]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(List: "array, boxed_slice"); @@ -52,3 +53,103 @@ impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser { Some(AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span: cx.attr_span }) } } + +pub(crate) struct ParenSugarParser; +impl<S: Stage> NoArgsAttributeParser<S> for ParenSugarParser { + const PATH: &[Symbol] = &[sym::rustc_paren_sugar]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::ParenSugar; +} + +pub(crate) struct TypeConstParser; +impl<S: Stage> NoArgsAttributeParser<S> for TypeConstParser { + const PATH: &[Symbol] = &[sym::type_const]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::TypeConst; +} + +// Markers + +pub(crate) struct MarkerParser; +impl<S: Stage> NoArgsAttributeParser<S> for MarkerParser { + const PATH: &[Symbol] = &[sym::marker]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::Marker; +} + +pub(crate) struct DenyExplicitImplParser; +impl<S: Stage> NoArgsAttributeParser<S> for DenyExplicitImplParser { + const PATH: &[Symbol] = &[sym::rustc_deny_explicit_impl]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::DenyExplicitImpl; +} + +pub(crate) struct DoNotImplementViaObjectParser; +impl<S: Stage> NoArgsAttributeParser<S> for DoNotImplementViaObjectParser { + const PATH: &[Symbol] = &[sym::rustc_do_not_implement_via_object]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::DoNotImplementViaObject; +} + +// FIXME(const_trait_impl): remove this +// Const traits + +pub(crate) struct ConstTraitParser; +impl<S: Stage> NoArgsAttributeParser<S> for ConstTraitParser { + const PATH: &[Symbol] = &[sym::const_trait]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::ConstTrait; +} + +// Specialization + +pub(crate) struct SpecializationTraitParser; +impl<S: Stage> NoArgsAttributeParser<S> for SpecializationTraitParser { + const PATH: &[Symbol] = &[sym::rustc_specialization_trait]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::SpecializationTrait; +} + +pub(crate) struct UnsafeSpecializationMarkerParser; +impl<S: Stage> NoArgsAttributeParser<S> for UnsafeSpecializationMarkerParser { + const PATH: &[Symbol] = &[sym::rustc_unsafe_specialization_marker]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::UnsafeSpecializationMarker; +} + +// Coherence + +pub(crate) struct CoinductiveParser; +impl<S: Stage> NoArgsAttributeParser<S> for CoinductiveParser { + const PATH: &[Symbol] = &[sym::rustc_coinductive]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::Coinductive; +} + +pub(crate) struct AllowIncoherentImplParser; +impl<S: Stage> NoArgsAttributeParser<S> for AllowIncoherentImplParser { + const PATH: &[Symbol] = &[sym::rustc_allow_incoherent_impl]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::AllowIncoherentImpl; +} + +pub(crate) struct CoherenceIsCoreParser; +impl<S: Stage> NoArgsAttributeParser<S> for CoherenceIsCoreParser { + const PATH: &[Symbol] = &[sym::rustc_coherence_is_core]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CoherenceIsCore; +} + +pub(crate) struct FundamentalParser; +impl<S: Stage> NoArgsAttributeParser<S> for FundamentalParser { + const PATH: &[Symbol] = &[sym::fundamental]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Fundamental; +} + +pub(crate) struct PointeeParser; +impl<S: Stage> NoArgsAttributeParser<S> for PointeeParser { + const PATH: &[Symbol] = &[sym::pointee]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::Pointee; +} diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index ce5ceb9139a..c9fdc57cc06 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -14,7 +14,7 @@ pub(crate) struct TransparencyParser; #[allow(rustc::diagnostic_outside_of_impl)] impl<S: Stage> SingleAttributeParser<S> for TransparencyParser { const PATH: &[Symbol] = &[sym::rustc_macro_transparency]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Custom(|cx, used, unused| { cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes"); }); diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 939f4a6fde7..1449680e35b 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -1,11 +1,10 @@ use std::cell::RefCell; use std::collections::BTreeMap; -use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; use private::Sealed; -use rustc_ast::{self as ast, MetaItemLit, NodeId}; +use rustc_ast::{self as ast, LitKind, MetaItemLit, NodeId}; use rustc_attr_data_structures::AttributeKind; use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind}; use rustc_errors::{DiagCtxtHandle, Diagnostic}; @@ -14,16 +13,24 @@ use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirI use rustc_session::Session; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; -use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser}; +use crate::attributes::allow_unstable::{ + AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser, +}; use crate::attributes::codegen_attrs::{ - ColdParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser, TargetFeatureParser, - TrackCallerParser, UsedParser, + ColdParser, ExportNameParser, NakedParser, NoMangleParser, OmitGdbPrettyPrinterSectionParser, + OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser, }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; +use crate::attributes::dummy::DummyParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; -use crate::attributes::link_attrs::{LinkNameParser, LinkSectionParser}; -use crate::attributes::lint_helpers::{AsPtrParser, PassByValueParser, PubTransparentParser}; +use crate::attributes::link_attrs::{ + ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, LinkOrdinalParser, + LinkSectionParser, StdInternalSymbolParser, +}; +use crate::attributes::lint_helpers::{ + AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser, +}; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; @@ -39,7 +46,12 @@ use crate::attributes::stability::{ BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser, }; use crate::attributes::test_attrs::IgnoreParser; -use crate::attributes::traits::SkipDuringMethodDispatchParser; +use crate::attributes::traits::{ + AllowIncoherentImplParser, CoherenceIsCoreParser, CoinductiveParser, ConstTraitParser, + DenyExplicitImplParser, DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, + ParenSugarParser, PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, + TypeConstParser, UnsafeSpecializationMarkerParser, +}; use crate::attributes::transparency::TransparencyParser; use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs}; use crate::parser::{ArgParser, MetaItemParser, PathParser}; @@ -123,14 +135,17 @@ attribute_parsers!( Combine<AllowInternalUnstableParser>, Combine<ReprParser>, Combine<TargetFeatureParser>, + Combine<UnstableFeatureBoundParser>, // tidy-alphabetical-end // tidy-alphabetical-start Single<DeprecationParser>, + Single<DummyParser>, Single<ExportNameParser>, Single<IgnoreParser>, Single<InlineParser>, Single<LinkNameParser>, + Single<LinkOrdinalParser>, Single<LinkSectionParser>, Single<MustUseParser>, Single<OptimizeParser>, @@ -141,18 +156,37 @@ attribute_parsers!( Single<RustcObjectLifetimeDefaultParser>, Single<SkipDuringMethodDispatchParser>, Single<TransparencyParser>, + Single<WithoutArgs<AllowIncoherentImplParser>>, Single<WithoutArgs<AsPtrParser>>, + Single<WithoutArgs<AutomaticallyDerivedParser>>, + Single<WithoutArgs<CoherenceIsCoreParser>>, + Single<WithoutArgs<CoinductiveParser>>, Single<WithoutArgs<ColdParser>>, Single<WithoutArgs<ConstContinueParser>>, Single<WithoutArgs<ConstStabilityIndirectParser>>, + Single<WithoutArgs<ConstTraitParser>>, + Single<WithoutArgs<DenyExplicitImplParser>>, + Single<WithoutArgs<DoNotImplementViaObjectParser>>, + Single<WithoutArgs<ExportStableParser>>, + Single<WithoutArgs<FfiConstParser>>, + Single<WithoutArgs<FfiPureParser>>, + Single<WithoutArgs<FundamentalParser>>, Single<WithoutArgs<LoopMatchParser>>, + Single<WithoutArgs<MarkerParser>>, Single<WithoutArgs<MayDangleParser>>, Single<WithoutArgs<NoImplicitPreludeParser>>, Single<WithoutArgs<NoMangleParser>>, Single<WithoutArgs<NonExhaustiveParser>>, + Single<WithoutArgs<OmitGdbPrettyPrinterSectionParser>>, + Single<WithoutArgs<ParenSugarParser>>, Single<WithoutArgs<PassByValueParser>>, + Single<WithoutArgs<PointeeParser>>, Single<WithoutArgs<PubTransparentParser>>, + Single<WithoutArgs<SpecializationTraitParser>>, + Single<WithoutArgs<StdInternalSymbolParser>>, Single<WithoutArgs<TrackCallerParser>>, + Single<WithoutArgs<TypeConstParser>>, + Single<WithoutArgs<UnsafeSpecializationMarkerParser>>, // tidy-alphabetical-end ]; ); @@ -171,7 +205,11 @@ pub trait Stage: Sized + 'static + Sealed { fn parsers() -> &'static group_type!(Self); - fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed; + fn emit_err<'sess>( + &self, + sess: &'sess Session, + diag: impl for<'x> Diagnostic<'x>, + ) -> ErrorGuaranteed; } // allow because it's a sealed trait @@ -183,8 +221,16 @@ impl Stage for Early { fn parsers() -> &'static group_type!(Self) { &early::ATTRIBUTE_PARSERS } - fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { - sess.dcx().create_err(diag).delay_as_bug() + fn emit_err<'sess>( + &self, + sess: &'sess Session, + diag: impl for<'x> Diagnostic<'x>, + ) -> ErrorGuaranteed { + if self.emit_errors.should_emit() { + sess.dcx().emit_err(diag) + } else { + sess.dcx().create_err(diag).delay_as_bug() + } } } @@ -197,20 +243,29 @@ impl Stage for Late { fn parsers() -> &'static group_type!(Self) { &late::ATTRIBUTE_PARSERS } - fn emit_err<'sess>(tcx: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { + fn emit_err<'sess>( + &self, + tcx: &'sess Session, + diag: impl for<'x> Diagnostic<'x>, + ) -> ErrorGuaranteed { tcx.dcx().emit_err(diag) } } /// used when parsing attributes for miscellaneous things *before* ast lowering -pub struct Early; +pub struct Early { + /// Whether to emit errors or delay them as a bug + /// For most attributes, the attribute will be parsed again in the `Late` stage and in this case the errors should be delayed + /// But for some, such as `cfg`, the attribute will be removed before the `Late` stage so errors must be emitted + pub emit_errors: ShouldEmit, +} /// used when parsing attributes during ast lowering pub struct Late; /// Context given to every attribute parser when accepting /// /// Gives [`AttributeParser`]s enough information to create errors, for example. -pub(crate) struct AcceptContext<'f, 'sess, S: Stage> { +pub struct AcceptContext<'f, 'sess, S: Stage> { pub(crate) shared: SharedContext<'f, 'sess, S>, /// The span of the attribute currently being parsed pub(crate) attr_span: Span, @@ -226,7 +281,7 @@ pub(crate) struct AcceptContext<'f, 'sess, S: Stage> { impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { - S::emit_err(&self.sess, diag) + self.stage.emit_err(&self.sess, diag) } /// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing @@ -441,7 +496,7 @@ impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> { /// /// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create /// errors, for example. -pub(crate) struct SharedContext<'p, 'sess, S: Stage> { +pub struct SharedContext<'p, 'sess, S: Stage> { /// The parse context, gives access to the session and the /// diagnostics context. pub(crate) cx: &'p mut AttributeParser<'sess, S>, @@ -503,13 +558,32 @@ pub enum OmitDoc { Skip, } +#[derive(Copy, Clone)] +pub enum ShouldEmit { + /// The operation will emit errors and lints. + /// This is usually what you need. + ErrorsAndLints, + /// The operation will emit *not* errors and lints. + /// Use this if you are *sure* that this operation will be called at a different time with `ShouldEmit::Emit`. + Nothing, +} + +impl ShouldEmit { + pub fn should_emit(&self) -> bool { + match self { + ShouldEmit::ErrorsAndLints => true, + ShouldEmit::Nothing => false, + } + } +} + /// Context created once, for example as part of the ast lowering /// context, through which all attributes can be lowered. pub struct AttributeParser<'sess, S: Stage = Late> { pub(crate) tools: Vec<Symbol>, features: Option<&'sess Features>, sess: &'sess Session, - stage: PhantomData<S>, + stage: S, /// *Only* parse attributes with this symbol. /// @@ -538,13 +612,14 @@ impl<'sess> AttributeParser<'sess, Early> { sym: Symbol, target_span: Span, target_node_id: NodeId, + features: Option<&'sess Features>, ) -> Option<Attribute> { let mut p = Self { - features: None, + features, tools: Vec::new(), parse_only: Some(sym), sess, - stage: PhantomData, + stage: Early { emit_errors: ShouldEmit::Nothing }, }; let mut parsed = p.parse_attribute_list( attrs, @@ -560,11 +635,55 @@ impl<'sess> AttributeParser<'sess, Early> { parsed.pop() } + + pub fn parse_single<T>( + sess: &'sess Session, + attr: &ast::Attribute, + target_span: Span, + target_node_id: NodeId, + features: Option<&'sess Features>, + emit_errors: ShouldEmit, + parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T, + template: &AttributeTemplate, + ) -> T { + let mut parser = Self { + features, + tools: Vec::new(), + parse_only: None, + sess, + stage: Early { emit_errors }, + }; + let ast::AttrKind::Normal(normal_attr) = &attr.kind else { + panic!("parse_single called on a doc attr") + }; + let meta_parser = MetaItemParser::from_attr(normal_attr, parser.dcx()); + let path = meta_parser.path(); + let args = meta_parser.args(); + let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext { + shared: SharedContext { + cx: &mut parser, + target_span, + target_id: target_node_id, + emit_lint: &mut |_lint| { + panic!("can't emit lints here for now (nothing uses this atm)"); + }, + }, + attr_span: attr.span, + template, + attr_path: path.get_attribute_path(), + }; + parse_fn(&mut cx, args) + } } impl<'sess, S: Stage> AttributeParser<'sess, S> { - pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self { - Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData } + pub fn new( + sess: &'sess Session, + features: &'sess Features, + tools: Vec<Symbol>, + stage: S, + ) -> Self { + Self { features: Some(features), tools, parse_only: None, sess, stage } } pub(crate) fn sess(&self) -> &'sess Session { @@ -575,6 +694,10 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { self.features.expect("features not available at this point in the compiler") } + pub(crate) fn features_option(&self) -> Option<&'sess Features> { + self.features + } + pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> { self.sess().dcx() } @@ -709,6 +832,11 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { attributes } + /// Returns whether there is a parser for an attribute with this name + pub fn is_parsed_attribute(path: &[Symbol]) -> bool { + Late::parsers().0.contains_key(path) + } + fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs { match args { ast::AttrArgs::Empty => AttrArgs::Empty, @@ -741,3 +869,32 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { } } } + +/// Parse a single integer. +/// +/// Used by attributes that take a single integer as argument, such as +/// `#[link_ordinal]` and `#[rustc_layout_scalar_valid_range_start]`. +/// `cx` is the context given to the attribute. +/// `args` is the parser for the attribute arguments. +pub(crate) fn parse_single_integer<S: Stage>( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser<'_>, +) -> Option<u128> { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span); + return None; + }; + let Some(single) = list.single() else { + cx.expected_single_argument(list.span); + return None; + }; + let Some(lit) = single.lit() else { + cx.expected_integer_literal(single.span()); + return None; + }; + let LitKind::Int(num, _ty) = lit.kind else { + cx.expected_integer_literal(single.span()); + return None; + }; + Some(num.0) +} diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 47eeb63bad3..dc54cb6b840 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -90,11 +90,12 @@ mod lints; pub mod parser; mod session_diagnostics; -pub use attributes::cfg::*; +pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr}; +pub use attributes::cfg_old::*; pub use attributes::util::{ find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version, }; -pub use context::{AttributeParser, Early, Late, OmitDoc}; +pub use context::{AttributeParser, Early, Late, OmitDoc, ShouldEmit}; pub use lints::emit_attribute_lint; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 28f6786f37f..5b0bf0e6662 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -504,6 +504,14 @@ pub(crate) struct UnrecognizedReprHint { } #[derive(Diagnostic)] +#[diag(attr_parsing_unstable_feature_bound_incompatible_stability)] +#[help] +pub(crate) struct UnstableFeatureBoundIncompatibleStability { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(attr_parsing_naked_functions_incompatible_attribute, code = E0736)] pub(crate) struct NakedFunctionIncompatibleAttribute { #[primary_span] @@ -514,6 +522,15 @@ pub(crate) struct NakedFunctionIncompatibleAttribute { pub attr: String, } +#[derive(Diagnostic)] +#[diag(attr_parsing_link_ordinal_out_of_range)] +#[note] +pub(crate) struct LinkOrdinalOutOfRange { + #[primary_span] + pub span: Span, + pub ordinal: u128, +} + pub(crate) enum AttributeParseErrorReason { ExpectedNoArgs, ExpectedStringLiteral { byte_string: Option<Span> }, @@ -584,7 +601,13 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { diag.code(E0565); } AttributeParseErrorReason::ExpectedNameValue(None) => { - // The suggestion we add below this match already contains enough information + // If the span is the entire attribute, the suggestion we add below this match already contains enough information + if self.span != self.attr_span { + diag.span_label( + self.span, + format!("expected this to be of the form `... = \"...\"`"), + ); + } } AttributeParseErrorReason::ExpectedNameValue(Some(name)) => { diag.span_label( |
