diff options
| author | Tshepang Mbambo <hopsi@tuta.io> | 2025-07-21 11:16:20 +0200 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-21 11:16:20 +0200 | 
| commit | 62e3a05291ef782cf212675d9e13c0b937c788c8 (patch) | |
| tree | 57ddd8cbcb5f32bd2773165cc53b9c69d65ed720 /compiler/rustc_attr_parsing/src | |
| parent | 4a45ab5ad4d6955285f79f331e677ecb5cdb60f1 (diff) | |
| parent | ad20b064c30bbe9626e8c99f2b455cc6fd836d18 (diff) | |
| download | rust-62e3a05291ef782cf212675d9e13c0b937c788c8.tar.gz rust-62e3a05291ef782cf212675d9e13c0b937c788c8.zip | |
Merge pull request #2512 from rust-lang/rustc-pull
Rustc pull update
Diffstat (limited to 'compiler/rustc_attr_parsing/src')
8 files changed, 211 insertions, 39 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 a56855b3bd3..6373cf6e08a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -9,7 +9,7 @@ use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; use thin_vec::ThinVec; -use crate::context::{AcceptContext, Stage}; +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, @@ -90,7 +90,7 @@ fn parse_cfg_entry_version<S: Stage>( list: &MetaItemListParser<'_>, meta_span: Span, ) -> Option<CfgEntry> { - try_gate_cfg(sym::version, meta_span, cx.sess(), Some(cx.features())); + 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; @@ -119,7 +119,9 @@ fn parse_cfg_entry_target<S: Stage>( list: &MetaItemListParser<'_>, meta_span: Span, ) -> Option<CfgEntry> { - if !cx.features().cfg_target_compact() { + if let Some(features) = cx.features_option() + && !features.cfg_target_compact() + { feature_err( cx.sess(), sym::cfg_target_compact, @@ -186,12 +188,13 @@ pub fn eval_config_entry( 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); + 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); @@ -202,7 +205,7 @@ pub fn eval_config_entry( CfgEntry::Any(subs, span) => { let mut any = None; for sub in subs { - let res = eval_config_entry(sess, sub, id, features); + 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); @@ -214,7 +217,7 @@ pub fn eval_config_entry( }) } CfgEntry::Not(sub, span) => { - if eval_config_entry(sess, sub, id, features).as_bool() { + if eval_config_entry(sess, sub, id, features, emit_lints).as_bool() { EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } } else { EvalConfigResult::True @@ -228,24 +231,28 @@ pub fn eval_config_entry( } } CfgEntry::NameValue { name, name_span, value, span } => { - match sess.psess.check_config.expecteds.get(name) { - Some(ExpectedValues::Some(values)) if !values.contains(&value.map(|(v, _)| v)) => { - id.emit_span_lint( - sess, - UNEXPECTED_CFGS, - *span, - BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value), - ); + if let ShouldEmit::ErrorsAndLints = emit_lints { + match sess.psess.check_config.expecteds.get(name) { + Some(ExpectedValues::Some(values)) + if !values.contains(&value.map(|(v, _)| v)) => + { + id.emit_span_lint( + sess, + UNEXPECTED_CFGS, + *span, + BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value), + ); + } + None if sess.psess.check_config.exhaustive_names => { + id.emit_span_lint( + sess, + UNEXPECTED_CFGS, + *span, + BuiltinLintDiag::UnexpectedCfgName((*name, *name_span), *value), + ); + } + _ => { /* not unexpected */ } } - 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))) { diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 34d9b048348..3e542771d58 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -1,4 +1,4 @@ -use rustc_attr_data_structures::{AttributeKind, OptimizeAttr, UsedBy}; +use rustc_attr_data_structures::{AttributeKind, CoverageStatus, OptimizeAttr, UsedBy}; use rustc_feature::{AttributeTemplate, template}; use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; @@ -52,6 +52,45 @@ impl<S: Stage> NoArgsAttributeParser<S> for ColdParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::Cold; } +pub(crate) struct CoverageParser; + +impl<S: Stage> SingleAttributeParser<S> for CoverageParser { + const PATH: &[Symbol] = &[sym::coverage]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + let Some(args) = args.list() else { + cx.expected_specific_argument_and_list(cx.attr_span, vec!["on", "off"]); + return None; + }; + + let Some(arg) = args.single() else { + cx.expected_single_argument(args.span); + return None; + }; + + let fail_incorrect_argument = |span| cx.expected_specific_argument(span, vec!["on", "off"]); + + let Some(arg) = arg.meta_item() else { + fail_incorrect_argument(args.span); + return None; + }; + + let status = match arg.path().word_sym() { + Some(sym::off) => CoverageStatus::Off, + Some(sym::on) => CoverageStatus::On, + None | Some(_) => { + fail_incorrect_argument(arg.span()); + return None; + } + }; + + Some(AttributeKind::Coverage(cx.attr_span, status)) + } +} + pub(crate) struct ExportNameParser; impl<S: Stage> SingleAttributeParser<S> for ExportNameParser { diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 6bccd0042a8..59337749c87 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 }) @@ -282,12 +292,12 @@ pub(crate) fn parse_stability<S: Stage>( } else if let Some(version) = parse_version(since) { StableSince::Version(version) } else { - cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span }); - StableSince::Err + let err = cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span }); + StableSince::Err(err) } } else { - cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span }); - StableSince::Err + let err = cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span }); + StableSince::Err(err) }; match feature { diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index d5e088effd5..e69a533699b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -91,6 +91,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for DoNotImplementViaObjectParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::DoNotImplementViaObject; } +// FIXME(const_trait_impl): remove this // Const traits pub(crate) struct ConstTraitParser; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 567341d1517..4d692d9562c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -13,10 +13,13 @@ 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, OmitGdbPrettyPrinterSectionParser, - OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser, + ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, + OmitGdbPrettyPrinterSectionParser, OptimizeParser, TargetFeatureParser, TrackCallerParser, + UsedParser, }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; @@ -133,9 +136,11 @@ attribute_parsers!( Combine<AllowInternalUnstableParser>, Combine<ReprParser>, Combine<TargetFeatureParser>, + Combine<UnstableFeatureBoundParser>, // tidy-alphabetical-end // tidy-alphabetical-start + Single<CoverageParser>, Single<DeprecationParser>, Single<DummyParser>, Single<ExportNameParser>, @@ -223,7 +228,7 @@ impl Stage for Early { sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>, ) -> ErrorGuaranteed { - if self.emit_errors { + if self.emit_errors.should_emit() { sess.dcx().emit_err(diag) } else { sess.dcx().create_err(diag).delay_as_bug() @@ -254,7 +259,7 @@ 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: bool, + pub emit_errors: ShouldEmit, } /// used when parsing attributes during ast lowering pub struct Late; @@ -449,6 +454,25 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { reason: AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings: false, + list: false, + }, + }) + } + + pub(crate) fn expected_specific_argument_and_list( + &self, + span: Span, + possibilities: Vec<&'static str>, + ) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedSpecificArgument { + possibilities, + strings: false, + list: true, }, }) } @@ -466,6 +490,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { reason: AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings: true, + list: false, }, }) } @@ -555,6 +580,25 @@ 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> { @@ -597,7 +641,7 @@ impl<'sess> AttributeParser<'sess, Early> { tools: Vec::new(), parse_only: Some(sym), sess, - stage: Early { emit_errors: false }, + stage: Early { emit_errors: ShouldEmit::Nothing }, }; let mut parsed = p.parse_attribute_list( attrs, @@ -620,7 +664,7 @@ impl<'sess> AttributeParser<'sess, Early> { target_span: Span, target_node_id: NodeId, features: Option<&'sess Features>, - emit_errors: bool, + emit_errors: ShouldEmit, parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T, template: &AttributeTemplate, ) -> T { diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 2102a26108b..dc54cb6b840 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -95,7 +95,7 @@ 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 97bf3d1c549..9a400e0fe10 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] @@ -525,7 +533,9 @@ pub(crate) struct LinkOrdinalOutOfRange { pub(crate) enum AttributeParseErrorReason { ExpectedNoArgs, - ExpectedStringLiteral { byte_string: Option<Span> }, + ExpectedStringLiteral { + byte_string: Option<Span>, + }, ExpectedIntegerLiteral, ExpectedAtLeastOneArgument, ExpectedSingleArgument, @@ -533,7 +543,12 @@ pub(crate) enum AttributeParseErrorReason { UnexpectedLiteral, ExpectedNameValue(Option<Symbol>), DuplicateKey(Symbol), - ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool }, + ExpectedSpecificArgument { + possibilities: Vec<&'static str>, + strings: bool, + /// Should we tell the user to write a list when they didn't? + list: bool, + }, } pub(crate) struct AttributeParseError { @@ -607,7 +622,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { format!("expected this to be of the form `{name} = \"...\"`"), ); } - AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings } => { + AttributeParseErrorReason::ExpectedSpecificArgument { + possibilities, + strings, + list: false, + } => { let quote = if strings { '"' } else { '`' }; match possibilities.as_slice() { &[] => {} @@ -633,6 +652,38 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { } } } + AttributeParseErrorReason::ExpectedSpecificArgument { + possibilities, + strings, + list: true, + } => { + let quote = if strings { '"' } else { '`' }; + match possibilities.as_slice() { + &[] => {} + &[x] => { + diag.span_label( + self.span, + format!( + "this attribute is only valid with {quote}{x}{quote} as an argument" + ), + ); + } + [first, second] => { + diag.span_label(self.span, format!("this attribute is only valid with either {quote}{first}{quote} or {quote}{second}{quote} as an argument")); + } + [first @ .., second_to_last, last] => { + let mut res = String::new(); + for i in first { + res.push_str(&format!("{quote}{i}{quote}, ")); + } + res.push_str(&format!( + "{quote}{second_to_last}{quote} or {quote}{last}{quote}" + )); + + diag.span_label(self.span, format!("this attribute is only valid with one of the following arguments: {res}")); + } + } + } } let suggestions = self.template.suggestions(false, &name); | 
