From 90a545b20bba1d508d58041d463835e93de3b3d1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 15 Mar 2022 20:52:56 -0700 Subject: [PATCH 1/2] Support multiple unstable attributes --- compiler/rustc_attr/src/builtin.rs | 146 +++++++++++++----- .../src/const_eval/fn_queries.rs | 11 -- .../src/transform/check_consts/check.rs | 101 +++++++----- .../src/transform/check_consts/ops.rs | 18 +-- compiler/rustc_expand/src/base.rs | 24 +-- compiler/rustc_metadata/src/rmeta/decoder.rs | 15 ++ compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_middle/src/arena.rs | 3 + compiler/rustc_middle/src/middle/stability.rs | 134 ++++++++-------- compiler/rustc_middle/src/query/mod.rs | 6 +- compiler/rustc_middle/src/ty/context.rs | 41 ++++- compiler/rustc_passes/src/stability.rs | 123 +++++++++------ compiler/rustc_resolve/src/macros.rs | 13 +- .../rustc_typeck/src/check/method/probe.rs | 20 +-- src/librustdoc/clean/inline.rs | 4 +- src/librustdoc/clean/mod.rs | 9 +- src/librustdoc/clean/types.rs | 4 +- src/librustdoc/html/format.rs | 5 +- src/librustdoc/html/render/mod.rs | 51 +++--- src/librustdoc/html/render/print_item.rs | 12 +- src/librustdoc/lib.rs | 2 +- src/test/rustdoc/stability.rs | 8 + .../stability-attribute-sanity.rs | 8 +- .../stability-attribute-sanity.stderr | 28 +--- .../ui/stability-attribute/two_unstables.rs | 11 ++ .../clippy_utils/src/qualify_min_const_fn.rs | 2 +- 26 files changed, 482 insertions(+), 319 deletions(-) create mode 100644 src/test/ui/stability-attribute/two_unstables.rs diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 8a134bf7f9662..2e1c6358c5dd4 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -94,39 +94,66 @@ pub enum OptimizeAttr { /// /// - `#[stable]` /// - `#[unstable]` -#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)] #[derive(HashStable_Generic)] pub struct Stability { pub level: StabilityLevel, - pub feature: Symbol, } /// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes. -#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)] #[derive(HashStable_Generic)] pub struct ConstStability { pub level: StabilityLevel, - pub feature: Symbol, /// whether the function has a `#[rustc_promotable]` attribute pub promotable: bool, } /// The available stability levels. -#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] +#[derive(Encodable, Decodable, PartialEq, Clone, Debug, Eq, Hash)] #[derive(HashStable_Generic)] pub enum StabilityLevel { - // Reason for the current stability level and the relevant rust-lang issue - Unstable { reason: Option, issue: Option, is_soft: bool }, - Stable { since: Symbol }, + Unstable { unstables: Vec, is_soft: bool }, + Stable { feature: Symbol, since: Symbol, span: Span }, +} + +impl StabilityLevel { + pub fn spans(&self) -> Vec { + match self { + StabilityLevel::Stable { span, .. } => vec![*span], + StabilityLevel::Unstable { unstables, .. } => { + unstables.iter().map(|u| u.span).collect() + } + } + } +} + +/// An instance of the `#[unstable]` attribute +#[derive(Encodable, Decodable, PartialEq, Clone, Debug, Eq, Hash)] +#[derive(HashStable_Generic)] +pub struct Unstability { + pub feature: Symbol, + pub issue: Option, + pub reason: Option, + pub span: Span, } impl StabilityLevel { pub fn is_unstable(&self) -> bool { matches!(self, StabilityLevel::Unstable { .. }) } + pub fn is_stable(&self) -> bool { matches!(self, StabilityLevel::Stable { .. }) } + + pub fn has_unstable_feature(&self, sym: Symbol) -> bool { + if let StabilityLevel::Unstable { unstables, .. } = &self { + unstables.iter().any(|u| u.feature == sym) + } else { + false + } + } } /// Collects stability info from all stability attributes in `attrs`. @@ -135,7 +162,7 @@ pub fn find_stability( sess: &Session, attrs: &[Attribute], item_sp: Span, -) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>) { +) -> (Option, Option) { find_stability_generic(sess, attrs.iter(), item_sp) } @@ -143,14 +170,14 @@ fn find_stability_generic<'a, I>( sess: &Session, attrs_iter: I, item_sp: Span, -) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>) +) -> (Option, Option) where I: Iterator, { use StabilityLevel::*; - let mut stab: Option<(Stability, Span)> = None; - let mut const_stab: Option<(ConstStability, Span)> = None; + let mut stab: Option = None; + let mut const_stab: Option = None; let mut promotable = false; let diagnostic = &sess.parse_sess.span_diagnostic; @@ -198,22 +225,6 @@ where let meta_name = meta.name_or_empty(); match meta_name { sym::rustc_const_unstable | sym::unstable => { - if meta_name == sym::unstable && stab.is_some() { - handle_errors( - &sess.parse_sess, - attr.span, - AttrError::MultipleStabilityLevels, - ); - break; - } else if meta_name == sym::rustc_const_unstable && const_stab.is_some() { - handle_errors( - &sess.parse_sess, - attr.span, - AttrError::MultipleStabilityLevels, - ); - break; - } - let mut feature = None; let mut reason = None; let mut issue = None; @@ -308,14 +319,70 @@ where ); continue; } - let level = Unstable { reason, issue: issue_num, is_soft }; + let unstability = + Unstability { feature, issue: issue_num, reason, span: attr.span }; if sym::unstable == meta_name { - stab = Some((Stability { level, feature }, attr.span)); + match &mut stab { + stab @ None => { + *stab = Some(Stability { + level: StabilityLevel::Unstable { + unstables: vec![unstability], + is_soft, + }, + }) + } + // Merge multiple unstability attributes into one + Some(Stability { + level: + StabilityLevel::Unstable { unstables, is_soft: old_is_soft }, + }) => { + unstables.push(unstability); + // FIXME(compiler-errors): Do we want this behavior: is_soft iff all are is_soft? + *old_is_soft &= is_soft; + } + _ => { + handle_errors( + &sess.parse_sess, + attr.span, + AttrError::MultipleStabilityLevels, + ); + } + } } else { - const_stab = Some(( - ConstStability { level, feature, promotable: false }, - attr.span, - )); + match &mut const_stab { + const_stab @ None => { + *const_stab = Some(ConstStability { + level: StabilityLevel::Unstable { + unstables: vec![unstability], + is_soft, + }, + promotable: false, + }) + } + Some(ConstStability { + level: + StabilityLevel::Unstable { + unstables: unstability, + is_soft: old_is_soft, + }, + .. + }) => { + unstability.push(Unstability { + feature, + issue: issue_num, + reason, + span: attr.span, + }); + *old_is_soft &= is_soft; + } + _ => { + handle_errors( + &sess.parse_sess, + attr.span, + AttrError::MultipleStabilityLevels, + ); + } + } } } (None, _, _) => { @@ -386,14 +453,11 @@ where match (feature, since) { (Some(feature), Some(since)) => { - let level = Stable { since }; + let level = Stable { since, feature, span: attr.span }; if sym::stable == meta_name { - stab = Some((Stability { level, feature }, attr.span)); + stab = Some(Stability { level }); } else { - const_stab = Some(( - ConstStability { level, feature, promotable: false }, - attr.span, - )); + const_stab = Some(ConstStability { level, promotable: false }); } } (None, _) => { @@ -413,7 +477,7 @@ where // Merge the const-unstable info into the stability info if promotable { - if let Some((ref mut stab, _)) = const_stab { + if let Some(stab) = &mut const_stab { stab.promotable = promotable; } else { struct_span_err!( diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index 05fbbf45d7c24..028c9619147e6 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -2,19 +2,8 @@ use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_span::symbol::Symbol; use rustc_target::spec::abi::Abi; -/// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it -pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - if tcx.is_const_fn_raw(def_id) { - let const_stab = tcx.lookup_const_stability(def_id)?; - if const_stab.level.is_unstable() { Some(const_stab.feature) } else { None } - } else { - None - } -} - pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let parent_id = tcx.hir().get_parent_node(hir_id); diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index eb01e261c1a55..3e2b74cd97d27 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -24,7 +24,6 @@ use super::ops::{self, NonConstOp, Status}; use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop, NeedsNonConstDrop}; use super::resolver::FlowSensitiveAnalysis; use super::{ConstCx, Qualif}; -use crate::const_eval::is_unstable_const_fn; type QualifResults<'mir, 'tcx, Q> = rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>; @@ -902,54 +901,74 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } // If the `const fn` we are trying to call is not const-stable, ensure that we have - // the proper feature gate enabled. - if let Some(gate) = is_unstable_const_fn(tcx, callee) { - trace!(?gate, "calling unstable const fn"); - if self.span.allows_unstable(gate) { - return; + // the proper feature gates enabled. + let gates = if tcx.is_const_fn_raw(callee) { + match tcx.lookup_const_stability(callee) { + Some(rustc_attr::ConstStability { + level: rustc_attr::StabilityLevel::Unstable { unstables, .. }, + .. + }) => unstables.as_slice(), + _ => &[], } + } else { + &[] + }; - // Calling an unstable function *always* requires that the corresponding gate - // be enabled, even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`. - if !tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == gate) { - self.check_op(ops::FnCallUnstable(callee, Some(gate))); - return; - } + if !gates.is_empty() { + trace!(?gates, "calling unstable const fn"); + // Features that are not enabled, but must be to make this call const + let mut bad_gates = vec![]; + for gate in gates { + let feature = gate.feature; - // If this crate is not using stability attributes, or the caller is not claiming to be a - // stable `const fn`, that is all that is required. - if !self.ccx.is_const_stable_const_fn() { - trace!("crate not using stability attributes or caller not stably const"); - return; - } + if self.span.allows_unstable(feature) { + continue; + } - // Otherwise, we are something const-stable calling a const-unstable fn. + // Calling an unstable function *always* requires that the corresponding gate + // be enabled, even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`. + if !tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == feature) + { + bad_gates.push(feature); + continue; + } + // If this crate is not using stability attributes, or the caller is not claiming to be a + // stable `const fn`, that is all that is required. + if !self.ccx.is_const_stable_const_fn() { + trace!( + "crate not using stability attributes or caller not stably const" + ); + continue; + } + // Otherwise, we are something const-stable calling a const-unstable fn. + if super::rustc_allow_const_fn_unstable(tcx, caller, feature) { + trace!("rustc_allow_const_fn_unstable gate active"); + continue; + } - if super::rustc_allow_const_fn_unstable(tcx, caller, gate) { - trace!("rustc_allow_const_fn_unstable gate active"); - return; + bad_gates.push(feature); } - - self.check_op(ops::FnCallUnstable(callee, Some(gate))); - return; - } - - // FIXME(ecstaticmorse); For compatibility, we consider `unstable` callees that - // have no `rustc_const_stable` attributes to be const-unstable as well. This - // should be fixed later. - let callee_is_unstable_unmarked = tcx.lookup_const_stability(callee).is_none() - && tcx.lookup_stability(callee).map_or(false, |s| s.level.is_unstable()); - if callee_is_unstable_unmarked { - trace!("callee_is_unstable_unmarked"); - // We do not use `const` modifiers for intrinsic "functions", as intrinsics are - // `extern` funtions, and these have no way to get marked `const`. So instead we - // use `rustc_const_(un)stable` attributes to mean that the intrinsic is `const` - if self.ccx.is_const_stable_const_fn() || is_intrinsic { - self.check_op(ops::FnCallUnstable(callee, None)); - return; + if !bad_gates.is_empty() { + self.check_op(ops::FnCallUnstable(callee, bad_gates)); + } + } else { + // FIXME(ecstaticmorse); For compatibility, we consider `unstable` callees that + // have no `rustc_const_stable` attributes to be const-unstable as well. This + // should be fixed later. + let callee_is_unstable_unmarked = tcx.lookup_const_stability(callee).is_none() + && tcx.lookup_stability(callee).map_or(false, |s| s.level.is_unstable()); + if callee_is_unstable_unmarked { + trace!("callee_is_unstable_unmarked"); + // We do not use `const` modifiers for intrinsic "functions", as intrinsics are + // `extern` funtions, and these have no way to get marked `const`. So instead we + // use `rustc_const_(un)stable` attributes to mean that the intrinsic is `const` + if self.ccx.is_const_stable_const_fn() || is_intrinsic { + self.check_op(ops::FnCallUnstable(callee, vec![])); + return; + } } + trace!("permitting call"); } - trace!("permitting call"); } // Forbid all `Drop` terminators unless the place being dropped is a local with no diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index ba248a3b6cbe5..8f695164a9655 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -325,7 +325,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { /// /// Contains the name of the feature that would allow the use of this function. #[derive(Debug)] -pub struct FnCallUnstable(pub DefId, pub Option); +pub struct FnCallUnstable(pub DefId, pub Vec); impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { fn build_error( @@ -333,22 +333,20 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let FnCallUnstable(def_id, feature) = *self; + let FnCallUnstable(def_id, features) = self; let mut err = ccx.tcx.sess.struct_span_err( span, - &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)), + &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(*def_id)), ); if ccx.is_const_stable_const_fn() { err.help("const-stable functions can only call other const-stable functions"); - } else if ccx.tcx.sess.is_nightly_build() { - if let Some(feature) = feature { - err.help(&format!( - "add `#![feature({})]` to the crate attributes to enable", - feature - )); - } + } else if ccx.tcx.sess.is_nightly_build() && !features.is_empty() { + err.help(&format!( + "add `#![feature({})]` to the crate attributes to enable", + features.iter().map(|s| s.to_string()).collect::>().join(", ") + )); } err diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index d6b308cdf85d5..cae6c09533cfa 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -776,16 +776,20 @@ impl SyntaxExtension { }) .unwrap_or_else(|| (None, helper_attrs)); let (stability, const_stability) = attr::find_stability(&sess, attrs, span); - if let Some((_, sp)) = const_stability { - sess.parse_sess + if let Some(stab) = const_stability { + let spans = stab.level.spans(); + let mut diag = sess + .parse_sess .span_diagnostic - .struct_span_err(sp, "macros cannot have const stability attributes") - .span_label(sp, "invalid const stability attribute") - .span_label( - sess.source_map().guess_head_span(span), - "const stability attribute affects this macro", - ) - .emit(); + .struct_span_err(spans.clone(), "macros cannot have const stability attributes"); + diag.span_label( + sess.source_map().guess_head_span(span), + "const stability attribute affects this macro", + ); + for sp in spans { + diag.span_label(sp, "invalid const stability attribute"); + } + diag.emit(); } SyntaxExtension { @@ -795,7 +799,7 @@ impl SyntaxExtension { .then(|| allow_internal_unstable.into()), allow_internal_unsafe: sess.contains_name(attrs, sym::allow_internal_unsafe), local_inner_macros, - stability: stability.map(|(s, _)| s), + stability, deprecation: attr::find_deprecation(&sess, attrs).map(|(d, _)| d), helper_attrs, edition, diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 2e6ce7b7040f9..a4ab6dad209e9 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -317,6 +317,21 @@ where } } +impl<'a, 'tcx, T> LazyQueryDecodable<'a, 'tcx, Option<&'tcx T>> for Option> +where + T: Decodable>, + T: ArenaAllocatable<'tcx>, +{ + fn decode_query( + self, + cdata: CrateMetadataRef<'a>, + tcx: TyCtxt<'tcx>, + _err: impl FnOnce() -> !, + ) -> Option<&'tcx T> { + self.map(|l| tcx.arena.alloc(l.decode((cdata, tcx))) as &_) + } +} + impl<'a, 'tcx, T> LazyQueryDecodable<'a, 'tcx, Option> for Option> where T: Decodable>, diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 924e5f921039e..cbb8297ed00c1 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1701,7 +1701,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } - Some(ProcMacroData { proc_macro_decls_static, stability, macros }) + Some(ProcMacroData { proc_macro_decls_static, stability: stability.cloned(), macros }) } else { None } diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index f6d8fc5b81f65..093bc3c772d9b 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -102,6 +102,9 @@ macro_rules! arena_types { [decode] impl_source: rustc_middle::traits::ImplSource<'tcx, ()>, [] dep_kind: rustc_middle::dep_graph::DepKindStruct, + + [] stability: rustc_attr::Stability, + [] const_stability: rustc_attr::ConstStability, ]); ) } diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 219af6caa1ae1..7e494ff1baea4 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -57,20 +57,20 @@ impl DeprecationEntry { /// A stability index, giving the stability level for items and methods. #[derive(HashStable, Debug)] -pub struct Index { +pub struct Index<'tcx> { /// This is mostly a cache, except the stabilities of local items /// are filled by the annotator. - pub stab_map: FxHashMap, - pub const_stab_map: FxHashMap, + pub stab_map: FxHashMap, + pub const_stab_map: FxHashMap, pub depr_map: FxHashMap, } -impl Index { - pub fn local_stability(&self, def_id: LocalDefId) -> Option { +impl<'tcx> Index<'tcx> { + pub fn local_stability(&self, def_id: LocalDefId) -> Option<&'tcx Stability> { self.stab_map.get(&def_id).copied() } - pub fn local_const_stability(&self, def_id: LocalDefId) -> Option { + pub fn local_const_stability(&self, def_id: LocalDefId) -> Option<&'tcx ConstStability> { self.const_stab_map.get(&def_id).copied() } @@ -81,42 +81,51 @@ impl Index { pub fn report_unstable( sess: &Session, - feature: Symbol, - reason: Option, - issue: Option, - suggestion: Option<(Span, String, String, Applicability)>, + unstables: &[attr::Unstability], + suggestion: Option<(Symbol, Span, String, String, Applicability)>, is_soft: bool, span: Span, - soft_handler: impl FnOnce(&'static Lint, Span, &str), + mut soft_handler: impl FnMut(&'static Lint, Span, &str), ) { - let msg = match reason { - Some(r) => format!("use of unstable library feature '{}': {}", feature, r), - None => format!("use of unstable library feature '{}'", &feature), - }; - - let msp: MultiSpan = span.into(); - let sm = &sess.parse_sess.source_map(); - let span_key = msp.primary_span().and_then(|sp: Span| { - if !sp.is_dummy() { - let file = sm.lookup_char_pos(sp.lo()).file; - if file.is_imported() { None } else { Some(span) } - } else { - None - } - }); + for attr::Unstability { feature, issue, reason, .. } in unstables { + let msg = match reason { + Some(r) => format!("use of unstable library feature '{}': {}", feature, r), + None => format!("use of unstable library feature '{}'", &feature), + }; - let error_id = (DiagnosticMessageId::StabilityId(issue), span_key, msg.clone()); - let fresh = sess.one_time_diagnostics.borrow_mut().insert(error_id); - if fresh { - if is_soft { - soft_handler(SOFT_UNSTABLE, span, &msg) - } else { - let mut err = - feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg); - if let Some((inner_types, ref msg, sugg, applicability)) = suggestion { - err.span_suggestion(inner_types, msg, sugg, applicability); + let msp: MultiSpan = span.into(); + let sm = &sess.parse_sess.source_map(); + let span_key = msp.primary_span().and_then(|sp: Span| { + if !sp.is_dummy() { + let file = sm.lookup_char_pos(sp.lo()).file; + if file.is_imported() { None } else { Some(span) } + } else { + None + } + }); + + let error_id = (DiagnosticMessageId::StabilityId(*issue), span_key, msg.clone()); + let fresh = sess.one_time_diagnostics.borrow_mut().insert(error_id); + if fresh { + if is_soft { + soft_handler(SOFT_UNSTABLE, span, &msg) + } else { + let mut err = feature_err_issue( + &sess.parse_sess, + *feature, + span, + GateIssue::Library(*issue), + &msg, + ); + if let Some((suggestion_feature, inner_types, msg, sugg, applicability)) = + &suggestion + { + if feature == suggestion_feature { + err.span_suggestion(*inner_types, &msg, sugg.to_string(), *applicability); + } + } + err.emit(); } - err.emit(); } } } @@ -263,17 +272,15 @@ fn late_report_deprecation( } /// Result of `TyCtxt::eval_stability`. -pub enum EvalResult { +pub enum EvalResult<'tcx> { /// We can use the item because it is stable or we provided the /// corresponding feature gate. Allow, /// We cannot use the item because it is unstable and we did not provide the /// corresponding feature gate. Deny { - feature: Symbol, - reason: Option, - issue: Option, - suggestion: Option<(Span, String, String, Applicability)>, + unstables: &'tcx [attr::Unstability], + suggestion: Option<(Symbol, Span, String, String, Applicability)>, is_soft: bool, }, /// The item does not have the `#[stable]` or `#[unstable]` marker assigned. @@ -302,7 +309,7 @@ fn suggestion_for_allocator_api( def_id: DefId, span: Span, feature: Symbol, -) -> Option<(Span, String, String, Applicability)> { +) -> Option<(Symbol, Span, String, String, Applicability)> { if feature == sym::allocator_api { if let Some(trait_) = tcx.parent(def_id) { if tcx.is_diagnostic_item(sym::Vec, trait_) { @@ -310,6 +317,7 @@ fn suggestion_for_allocator_api( let inner_types = sm.span_extend_to_prev_char(span, '<', true); if let Ok(snippet) = sm.span_to_snippet(inner_types) { return Some(( + sym::allocator_api, inner_types, "consider wrapping the inner types in tuple".to_string(), format!("({})", snippet), @@ -338,7 +346,7 @@ impl<'tcx> TyCtxt<'tcx> { id: Option, span: Span, method_span: Option, - ) -> EvalResult { + ) -> EvalResult<'tcx> { // Deprecated attributes apply in-crate and cross-crate. if let Some(id) = id { if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) { @@ -410,14 +418,11 @@ impl<'tcx> TyCtxt<'tcx> { } match stability { - Some(Stability { - level: attr::Unstable { reason, issue, is_soft }, feature, .. - }) => { - if span.allows_unstable(feature) { - debug!("stability: skipping span={:?} since it is internal", span); - return EvalResult::Allow; - } - if self.features().active(feature) { + Some(Stability { level: attr::Unstable { unstables, is_soft }, .. }) => { + if unstables + .iter() + .all(|u| span.allows_unstable(u.feature) || self.features().active(u.feature)) + { return EvalResult::Allow; } @@ -430,14 +435,20 @@ impl<'tcx> TyCtxt<'tcx> { // the `-Z force-unstable-if-unmarked` flag present (we're // compiling a compiler crate), then let this missing feature // annotation slide. - if feature == sym::rustc_private && issue == NonZeroU32::new(27812) { + if unstables + .iter() + .all(|u| u.feature == sym::rustc_private && u.issue == NonZeroU32::new(27812)) + { if self.sess.opts.debugging_opts.force_unstable_if_unmarked { return EvalResult::Allow; } } - let suggestion = suggestion_for_allocator_api(self, def_id, span, feature); - EvalResult::Deny { feature, reason, issue, suggestion, is_soft } + let suggestion = unstables + .iter() + .find_map(|u| suggestion_for_allocator_api(self, def_id, span, u.feature)); + + EvalResult::Deny { unstables: unstables.as_slice(), suggestion, is_soft: *is_soft } } Some(_) => { // Stable APIs are always ok to call and deprecated APIs are @@ -488,16 +499,9 @@ impl<'tcx> TyCtxt<'tcx> { }; match self.eval_stability(def_id, id, span, method_span) { EvalResult::Allow => {} - EvalResult::Deny { feature, reason, issue, suggestion, is_soft } => report_unstable( - self.sess, - feature, - reason, - issue, - suggestion, - is_soft, - span, - soft_handler, - ), + EvalResult::Deny { unstables, suggestion, is_soft } => { + report_unstable(self.sess, unstables, suggestion, is_soft, span, soft_handler) + } EvalResult::Unmarked => unmarked(span, def_id), } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index e07b174bc6aaa..3d79e5e4da778 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1018,12 +1018,12 @@ rustc_queries! { separate_provide_extern } - query lookup_stability(def_id: DefId) -> Option { + query lookup_stability(def_id: DefId) -> Option<&'tcx attr::Stability> { desc { |tcx| "looking up stability of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } - query lookup_const_stability(def_id: DefId) -> Option { + query lookup_const_stability(def_id: DefId) -> Option<&'tcx attr::ConstStability> { desc { |tcx| "looking up const stability of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } @@ -1643,7 +1643,7 @@ rustc_queries! { desc { |tcx| "names_imported_by_glob_use for `{}`", tcx.def_path_str(def_id.to_def_id()) } } - query stability_index(_: ()) -> stability::Index { + query stability_index(_: ()) -> stability::Index<'tcx> { storage(ArenaCacheSelector<'tcx>) eval_always desc { "calculating the stability index for the local crate" } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f51e6c2bc1f4d..eff8cc351d428 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1294,7 +1294,7 @@ impl<'tcx> TyCtxt<'tcx> { self.diagnostic_items(did.krate).name_to_id.get(&name) == Some(&did) } - pub fn stability(self) -> &'tcx stability::Index { + pub fn stability(self) -> &'tcx stability::Index<'tcx> { self.stability_index(()) } @@ -2687,6 +2687,17 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_substs(iter::once(self_ty.into()).chain(rest.iter().cloned())) } + pub fn mk_stability(self, stab: rustc_attr::Stability) -> &'tcx rustc_attr::Stability { + self.arena.alloc(stab) + } + + pub fn mk_const_stability( + self, + stab: rustc_attr::ConstStability, + ) -> &'tcx rustc_attr::ConstStability { + self.arena.alloc(stab) + } + pub fn mk_bound_variable_kinds< I: InternAs<[ty::BoundVariableKind], &'tcx List>, >( @@ -2791,13 +2802,18 @@ impl<'tcx> TyCtxt<'tcx> { pub fn is_const_fn(self, def_id: DefId) -> bool { if self.is_const_fn_raw(def_id) { match self.lookup_const_stability(def_id) { - Some(stability) if stability.level.is_unstable() => { + Some(rustc_attr::ConstStability { + level: rustc_attr::Unstable { unstables, .. }, + .. + }) => { // has a `rustc_const_unstable` attribute, check whether the user enabled the // corresponding feature gate. - self.features() - .declared_lib_features - .iter() - .any(|&(sym, _)| sym == stability.feature) + unstables.iter().all(|unstability| { + self.features() + .declared_lib_features + .iter() + .any(|&(sym, _)| sym == unstability.feature) + }) } // functions without const stability are either stable user written // const fn or the user is using feature gates and we thus don't @@ -2808,6 +2824,19 @@ impl<'tcx> TyCtxt<'tcx> { false } } + + /// Returns true if the function is declared `rustc_const_unstable`, even if the current + /// feature flags allow it to be called. + pub fn is_unstable_const_fn(self, def_id: DefId) -> bool { + if self.is_const_fn_raw(def_id) { + matches!( + self.lookup_const_stability(def_id), + Some(rustc_attr::ConstStability { level: rustc_attr::Unstable { .. }, .. }) + ) + } else { + false + } + } } impl<'tcx> TyCtxtAt<'tcx> { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 09be1dac6f163..0593678da8885 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -86,9 +86,9 @@ impl InheritStability { // A private tree-walker for producing an Index. struct Annotator<'a, 'tcx> { tcx: TyCtxt<'tcx>, - index: &'a mut Index, - parent_stab: Option, - parent_const_stab: Option, + index: &'a mut Index<'tcx>, + parent_stab: Option<&'tcx Stability>, + parent_const_stab: Option<&'tcx ConstStability>, parent_depr: Option, in_trait_impl: bool, } @@ -162,17 +162,15 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp); - let mut const_span = None; + // Intern stability + let stab = stab.map(|stab| self.tcx.mk_stability(stab)); - let const_stab = const_stab.map(|(const_stab, const_span_node)| { - self.index.const_stab_map.insert(def_id, const_stab); - const_span = Some(const_span_node); - const_stab - }); + // Intern const_stability + let const_stab = const_stab.map(|const_stab| self.tcx.mk_const_stability(const_stab)); // If the current node is a function, has const stability attributes and if it doesn not have an intrinsic ABI, // check if the function/method is const or the parent impl block is const - if let (Some(const_span), Some(fn_sig)) = (const_span, fn_sig) { + if let (Some(const_stab), Some(fn_sig)) = (const_stab, fn_sig) { if fn_sig.header.abi != Abi::RustIntrinsic && fn_sig.header.abi != Abi::PlatformIntrinsic && !fn_sig.header.is_const() @@ -180,14 +178,16 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if !self.in_trait_impl || (self.in_trait_impl && !self.tcx.is_const_fn_raw(def_id.to_def_id())) { - missing_const_err(&self.tcx.sess, fn_sig.span, const_span); + missing_const_err(&self.tcx.sess, fn_sig.span, const_stab.level.spans()); } } } // `impl const Trait for Type` items forward their const stability to their // immediate children. - if const_stab.is_none() { + if let Some(const_stab) = const_stab { + self.index.const_stab_map.insert(def_id, const_stab); + } else { debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab); if let Some(parent) = self.parent_const_stab { if parent.level.is_unstable() { @@ -209,22 +209,27 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } - let stab = stab.map(|(stab, span)| { + let stab = stab.map(|stab| { + let spans = stab.level.spans(); // Error if prohibited, or can't inherit anything from a container. if kind == AnnotationKind::Prohibited || (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated) { - self.tcx.sess.struct_span_err(span,"this stability annotation is useless") - .span_label(span, "useless stability annotation") - .span_label(item_sp, "the stability attribute annotates this item") - .emit(); + let mut diag = self + .tcx + .sess + .struct_span_err(spans.clone(), "this stability annotation is useless"); + for sp in &spans { + diag.span_label(*sp, "useless stability annotation"); + } + diag.span_label(item_sp, "the stability attribute annotates this item").emit(); } debug!("annotate: found {:?}", stab); // Check if deprecated_since < stable_since. If it is, // this is *almost surely* an accident. - if let (&Some(dep_since), &attr::Stable { since: stab_since }) = + if let (&Some(dep_since), &attr::Stable { since: stab_since, span, .. }) = (&depr.as_ref().and_then(|(d, _)| d.since), &stab.level) { // Explicit version of iter::order::lt to handle parse errors properly @@ -233,7 +238,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { { match stab_v.parse::() { Err(_) => { - self.tcx.sess.struct_span_err(span, "invalid stability version found") + self.tcx + .sess + .struct_span_err(span, "invalid stability version found") .span_label(span, "invalid stability version") .span_label(item_sp, "the stability attribute annotates this item") .emit(); @@ -242,9 +249,17 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { Ok(stab_vp) => match dep_v.parse::() { Ok(dep_vp) => match dep_vp.cmp(&stab_vp) { Ordering::Less => { - self.tcx.sess.struct_span_err(span, "an API can't be stabilized after it is deprecated") + self.tcx + .sess + .struct_span_err( + spans, + "an API can't be stabilized after it is deprecated", + ) .span_label(span, "invalid version") - .span_label(item_sp, "the stability attribute annotates this item") + .span_label( + item_sp, + "the stability attribute annotates this item", + ) .emit(); break; } @@ -253,9 +268,14 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { }, Err(_) => { if dep_v != "TBD" { - self.tcx.sess.struct_span_err(span, "invalid deprecation version found") + self.tcx + .sess + .struct_span_err(span, "invalid deprecation version found") .span_label(span, "invalid deprecation version") - .span_label(item_sp, "the stability attribute annotates this item") + .span_label( + item_sp, + "the stability attribute annotates this item", + ) .emit(); } break; @@ -271,7 +291,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if stab.is_none() { debug!("annotate: stab not found, parent = {:?}", self.parent_stab); - if let Some(stab) = self.parent_stab { + if let Some(stab) = &self.parent_stab { if inherit_deprecation.yes() && stab.level.is_unstable() || inherit_from_parent.yes() { @@ -291,8 +311,8 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { fn recurse_with_stability_attrs( &mut self, depr: Option, - stab: Option, - const_stab: Option, + stab: Option<&'tcx Stability>, + const_stab: Option<&'tcx ConstStability>, f: impl FnOnce(&mut Self), ) { // These will be `Some` if this item changes the corresponding stability attribute. @@ -606,7 +626,7 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { // stable (assuming they have not inherited instability from their parent). } -fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { +fn stability_index<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> Index<'tcx> { let mut index = Index { stab_map: Default::default(), const_stab_map: Default::default(), @@ -633,14 +653,17 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { let reason = "this crate is being loaded from the sysroot, an \ unstable location; did you mean to load this crate \ from crates.io via `Cargo.toml` instead?"; - let stability = Stability { + let stability = tcx.mk_stability(Stability { level: attr::StabilityLevel::Unstable { - reason: Some(Symbol::intern(reason)), - issue: NonZeroU32::new(27812), + unstables: vec![attr::Unstability { + reason: Some(Symbol::intern(reason)), + issue: NonZeroU32::new(27812), + feature: sym::rustc_private, + span: rustc_span::DUMMY_SP, + }], is_soft: false, }, - feature: sym::rustc_private, - }; + }); annotator.parent_stab = Some(stability); } @@ -717,22 +740,26 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // it will have no effect. // See: https://github.com/rust-lang/rust/issues/55436 let attrs = self.tcx.hir().attrs(item.hir_id()); - if let (Some((Stability { level: attr::Unstable { .. }, .. }, span)), _) = + if let (Some(Stability { level: attr::Unstable { unstables, .. }, .. }), _) = attr::find_stability(&self.tcx.sess, attrs, item.span) { let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; c.visit_ty(self_ty); c.visit_trait_ref(t); if c.fully_stable { - self.tcx.struct_span_lint_hir( - INEFFECTIVE_UNSTABLE_TRAIT_IMPL, - item.hir_id(), - span, - |lint| {lint - .build("an `#[unstable]` annotation here has no effect") - .note("see issue #55436 for more information") - .emit();} - ); + for u in unstables { + self.tcx.struct_span_lint_hir( + INEFFECTIVE_UNSTABLE_TRAIT_IMPL, + item.hir_id(), + u.span, + |lint| { + lint + .build("an `#[unstable]` annotation here has no effect") + .note("see issue #55436 for more information") + .emit(); + } + ); + } } } } @@ -934,14 +961,14 @@ fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) { .emit(); } -fn missing_const_err(session: &Session, fn_sig_span: Span, const_span: Span) { +fn missing_const_err(session: &Session, fn_sig_span: Span, const_spans: Vec) { const ERROR_MSG: &'static str = "attributes `#[rustc_const_unstable]` \ and `#[rustc_const_stable]` require \ the function or method to be `const`"; - session - .struct_span_err(fn_sig_span, ERROR_MSG) - .span_help(fn_sig_span, "make the function or method const") - .span_label(const_span, "attribute specified here") - .emit(); + let mut diag = session.struct_span_err(fn_sig_span, ERROR_MSG); + for sp in const_spans { + diag.span_label(sp, "attribute specified here"); + } + diag.span_help(fn_sig_span, "make the function or method const").emit(); } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index e34d3e605ecdf..098737968dbf6 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -1126,19 +1126,18 @@ impl<'a> Resolver<'a> { ) { let span = path.span; if let Some(stability) = &ext.stability { - if let StabilityLevel::Unstable { reason, issue, is_soft } = stability.level { - let feature = stability.feature; - if !self.active_features.contains(&feature) && !span.allows_unstable(feature) { + if let StabilityLevel::Unstable { unstables, is_soft } = &stability.level { + if !unstables.iter().all(|u| { + span.allows_unstable(u.feature) || self.active_features.contains(&u.feature) + }) { let lint_buffer = &mut self.lint_buffer; let soft_handler = |lint, span, msg: &_| lint_buffer.buffer_lint(lint, node_id, span, msg); stability::report_unstable( self.session, - feature, - reason, - issue, + unstables.as_slice(), None, - is_soft, + *is_soft, span, soft_handler, ); diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 4e1645adca5d3..aca301de983e3 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -1127,7 +1127,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn pick_all_method( &mut self, - mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Vec)>>, ) -> Option> { let steps = self.steps.clone(); steps @@ -1187,7 +1187,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &mut self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Vec)>>, ) -> Option> { if step.unsize { return None; @@ -1216,7 +1216,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, mutbl: hir::Mutability, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Vec)>>, ) -> Option> { let tcx = self.tcx; @@ -1241,7 +1241,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &mut self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Vec)>>, ) -> Option> { // Don't convert an unsized reference to ptr if step.unsize { @@ -1309,7 +1309,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn pick_method( &mut self, self_ty: Ty<'tcx>, - mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Vec)>>, ) -> Option> { if !self.tcx.sess.opts.debugging_opts.pick_stable_methods_before_any_unstable { return self.pick_method_with_unstable(self_ty); @@ -1351,7 +1351,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { Option>, Option>, )>, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Vec)>>, ) -> Option> where ProbesIter: Iterator> + Clone, @@ -1377,10 +1377,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if let Some(uc) = unstable_candidates { applicable_candidates.retain(|&(p, _)| { - if let stability::EvalResult::Deny { feature, .. } = + if let stability::EvalResult::Deny { unstables, .. } = self.tcx.eval_stability(p.item.def_id, None, self.span, None) { - uc.push((p.clone(), feature)); + uc.push((p.clone(), unstables.iter().map(|u| u.feature).collect())); return false; } true @@ -1404,7 +1404,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn emit_unstable_name_collision_hint( &self, stable_pick: &Pick<'_>, - unstable_candidates: &[(Candidate<'tcx>, Symbol)], + unstable_candidates: &[(Candidate<'tcx>, Vec)], ) { self.tcx.struct_span_lint_hir( lint::builtin::UNSTABLE_NAME_COLLISIONS, @@ -1448,7 +1448,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { for (candidate, feature) in unstable_candidates { diag.help(&format!( "add `#![feature({})]` to the crate attributes to enable `{}`", - feature, + feature.iter().map(|s| s.to_string()).collect::>().join(", "), self.tcx.def_path_str(candidate.item.def_id), )); } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index ec538f11831b0..fe026967a2b18 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -351,7 +351,7 @@ crate fn build_impl( } if let Some(stab) = tcx.lookup_stability(did) { - if stab.level.is_unstable() && stab.feature == sym::rustc_private { + if stab.level.has_unstable_feature(sym::rustc_private) { return; } } @@ -380,7 +380,7 @@ crate fn build_impl( } if let Some(stab) = tcx.lookup_stability(did) { - if stab.level.is_unstable() && stab.feature == sym::rustc_private { + if stab.level.has_unstable_feature(sym::rustc_private) { return; } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2c61b7468dcc8..ed9fc295880e8 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -12,7 +12,6 @@ crate mod utils; use rustc_ast as ast; use rustc_attr as attr; -use rustc_const_eval::const_eval::is_unstable_const_fn; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -797,7 +796,7 @@ fn clean_fn_or_proc_macro( let mut func = clean_function(cx, sig, generics, body_id); let def_id = item.def_id.to_def_id(); func.header.constness = - if cx.tcx.is_const_fn(def_id) && is_unstable_const_fn(cx.tcx, def_id).is_none() { + if cx.tcx.is_const_fn(def_id) && !cx.tcx.is_unstable_const_fn(def_id) { hir::Constness::Const } else { hir::Constness::NotConst @@ -982,7 +981,7 @@ impl Clean for hir::TraitItem<'_> { hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { let mut m = clean_function(cx, sig, &self.generics, body); if m.header.constness == hir::Constness::Const - && is_unstable_const_fn(cx.tcx, local_did).is_some() + && cx.tcx.is_unstable_const_fn(local_did) { m.header.constness = hir::Constness::NotConst; } @@ -998,7 +997,7 @@ impl Clean for hir::TraitItem<'_> { }); let mut t = Function { header: sig.header, decl, generics }; if t.header.constness == hir::Constness::Const - && is_unstable_const_fn(cx.tcx, local_did).is_some() + && cx.tcx.is_unstable_const_fn(local_did) { t.header.constness = hir::Constness::NotConst; } @@ -1031,7 +1030,7 @@ impl Clean for hir::ImplItem<'_> { hir::ImplItemKind::Fn(ref sig, body) => { let mut m = clean_function(cx, sig, &self.generics, body); if m.header.constness == hir::Constness::Const - && is_unstable_const_fn(cx.tcx, local_did).is_some() + && cx.tcx.is_unstable_const_fn(local_did) { m.header.constness = hir::Constness::NotConst; } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 47289eb8978b9..99e2a72a998cf 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -381,11 +381,11 @@ crate fn rustc_span(def_id: DefId, tcx: TyCtxt<'_>) -> Span { } impl Item { - crate fn stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option { + crate fn stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx Stability> { self.def_id.as_def_id().and_then(|did| tcx.lookup_stability(did)) } - crate fn const_stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option { + crate fn const_stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ConstStability> { self.def_id.as_def_id().and_then(|did| tcx.lookup_const_stability(did)) } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 5c59609d5b8c6..8ddcc0eaef4f5 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1397,7 +1397,10 @@ impl PrintWithSpace for hir::Mutability { } } -crate fn print_constness_with_space(c: &hir::Constness, s: Option) -> &'static str { +crate fn print_constness_with_space( + c: &hir::Constness, + s: Option<&ConstStability>, +) -> &'static str { match (c, s) { // const stable or when feature(staged_api) is not set ( diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 3666767a9d9cb..98119a82bdc15 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -667,25 +667,35 @@ fn short_item_info( // Render unstable items. But don't render "rustc_private" crates (internal compiler crates). // Those crates are permanently unstable so it makes no sense to render "unstable" everywhere. - if let Some((StabilityLevel::Unstable { reason: _, issue, .. }, feature)) = item - .stability(cx.tcx()) - .as_ref() - .filter(|stab| stab.feature != sym::rustc_private) - .map(|stab| (stab.level, stab.feature)) + if let Some(rustc_attr::Stability { + level: StabilityLevel::Unstable { unstables, .. }, .. + }) = item.stability(cx.tcx()) { let mut message = "🔬 This is a nightly-only experimental API.".to_owned(); + let unstables: Vec<_> = + unstables.iter().cloned().filter(|u| u.feature != sym::rustc_private).collect(); + if !unstables.is_empty() { + message.push_str(" ("); + + for (i, u) in unstables.iter().enumerate() { + let mut feature = format!("{}", Escape(u.feature.as_str())); + if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, u.issue) { + feature.push_str(&format!( + " #{issue}", + url = url, + issue = issue + )); + } - let mut feature = format!("{}", Escape(feature.as_str())); - if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) { - feature.push_str(&format!( - " #{issue}", - url = url, - issue = issue - )); - } + if i > 0 { + message.push_str(", "); + } + message.push_str(feature.as_str()); + } - message.push_str(&format!(" ({})", feature)); + message.push(')'); + } extra_info.push(format!("
{}
", message)); } @@ -905,7 +915,7 @@ fn assoc_method( fn render_stability_since_raw( w: &mut Buffer, ver: Option, - const_stability: Option, + const_stability: Option<&ConstStability>, containing_ver: Option, containing_const_ver: Option, ) -> bool { @@ -920,16 +930,17 @@ fn render_stability_since_raw( } let const_title_and_stability = match const_stability { - Some(ConstStability { level: StabilityLevel::Stable { since }, .. }) - if Some(since) != containing_const_ver => + Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) + if Some(*since) != containing_const_ver => { Some((format!("const since {}", since), format!("const: {}", since))) } - Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => { - let unstable = if let Some(n) = issue { + Some(ConstStability { level: StabilityLevel::Unstable { unstables, .. }, .. }) => { + // FIXME(compiler-errors): Should we link to several issues here? How? + let unstable = if let Some(u) = unstables.first() && let Some(n) = u.issue { format!( r#"unstable"#, - n, feature + n, u.feature ) } else { String::from("unstable") diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index e6c7745c6e10f..3d10e295e39bc 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -226,8 +226,8 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl { return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2)); } - let s1 = i1.stability(tcx).as_ref().map(|s| s.level); - let s2 = i2.stability(tcx).as_ref().map(|s| s.level); + let s1 = i1.stability(tcx).as_ref().map(|s| &s.level); + let s2 = i2.stability(tcx).as_ref().map(|s| &s.level); if let (Some(a), Some(b)) = (s1, s2) { match (a.is_stable(), b.is_stable()) { (true, true) | (false, false) => {} @@ -430,11 +430,9 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> // The "rustc_private" crates are permanently unstable so it makes no sense // to render "unstable" everywhere. - if item - .stability(tcx) - .as_ref() - .map(|s| s.level.is_unstable() && s.feature != sym::rustc_private) - == Some(true) + if let Some(rustc_attr::Stability { level: rustc_attr::Unstable { unstables, .. }, .. }) = item + .stability(tcx) + .as_ref() && !unstables.iter().any(|u| u.feature == sym::rustc_private) { tags += &tag_html("unstable", "", "Experimental"); } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index f59222b780d3e..db75e10bf87cc 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -10,6 +10,7 @@ #![feature(control_flow_enum)] #![feature(box_syntax)] #![feature(drain_filter)] +#![feature(let_chains)] #![feature(let_else)] #![feature(nll)] #![feature(test)] @@ -40,7 +41,6 @@ extern crate rustc_ast; extern crate rustc_ast_lowering; extern crate rustc_ast_pretty; extern crate rustc_attr; -extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; diff --git a/src/test/rustdoc/stability.rs b/src/test/rustdoc/stability.rs index 4ff06d9c9954e..1824874f640a8 100644 --- a/src/test/rustdoc/stability.rs +++ b/src/test/rustdoc/stability.rs @@ -10,3 +10,11 @@ pub struct Unstable { pub foo: u32, pub bar: u32, } + +#[unstable(feature = "test", issue = "1")] +#[unstable(feature = "test2", issue = "2")] +pub trait UnstableTrait { + // @has stability/trait.UnstableTrait.html \ + // '//div[@class="item-info"]//div[@class="stab unstable"]' \ + // 'This is a nightly-only experimental API. (test, test2)' +} diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.rs b/src/test/ui/stability-attribute/stability-attribute-sanity.rs index f37a8f328a75a..4d9754dc9a6a3 100644 --- a/src/test/ui/stability-attribute/stability-attribute-sanity.rs +++ b/src/test/ui/stability-attribute/stability-attribute-sanity.rs @@ -49,10 +49,6 @@ mod missing_version { #[stable(feature = "a", since = "b")] //~ ERROR multiple stability levels [E0544] fn multiple1() { } -#[unstable(feature = "b", issue = "none")] -#[unstable(feature = "b", issue = "none")] //~ ERROR multiple stability levels [E0544] -fn multiple2() { } - #[stable(feature = "a", since = "b")] #[stable(feature = "a", since = "b")] //~ ERROR multiple stability levels [E0544] fn multiple3() { } @@ -60,9 +56,7 @@ fn multiple3() { } #[stable(feature = "a", since = "b")] //~ ERROR invalid stability version found #[deprecated(since = "b", note = "text")] #[deprecated(since = "b", note = "text")] //~ ERROR multiple deprecated attributes -#[rustc_const_unstable(feature = "c", issue = "none")] -#[rustc_const_unstable(feature = "d", issue = "none")] //~ ERROR multiple stability levels -pub const fn multiple4() { } +pub fn multiple4() { } #[stable(feature = "a", since = "1.0.0")] //~ ERROR invalid deprecation version found //~^ ERROR feature `a` is declared stable since 1.0.0 diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr index 9e2d9f2708489..9764a74b966f7 100644 --- a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr +++ b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr @@ -73,40 +73,28 @@ LL | #[stable(feature = "a", since = "b")] error[E0544]: multiple stability levels --> $DIR/stability-attribute-sanity.rs:53:1 | -LL | #[unstable(feature = "b", issue = "none")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0544]: multiple stability levels - --> $DIR/stability-attribute-sanity.rs:57:1 - | LL | #[stable(feature = "a", since = "b")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0550]: multiple deprecated attributes - --> $DIR/stability-attribute-sanity.rs:62:1 + --> $DIR/stability-attribute-sanity.rs:58:1 | LL | #[deprecated(since = "b", note = "text")] | ----------------------------------------- first deprecation attribute LL | #[deprecated(since = "b", note = "text")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ repeated deprecation attribute -error[E0544]: multiple stability levels - --> $DIR/stability-attribute-sanity.rs:64:1 - | -LL | #[rustc_const_unstable(feature = "d", issue = "none")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: invalid stability version found - --> $DIR/stability-attribute-sanity.rs:60:1 + --> $DIR/stability-attribute-sanity.rs:56:1 | LL | #[stable(feature = "a", since = "b")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid stability version ... -LL | pub const fn multiple4() { } - | ---------------------------- the stability attribute annotates this item +LL | pub fn multiple4() { } + | ---------------------- the stability attribute annotates this item error: invalid deprecation version found - --> $DIR/stability-attribute-sanity.rs:67:1 + --> $DIR/stability-attribute-sanity.rs:61:1 | LL | #[stable(feature = "a", since = "1.0.0")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid deprecation version @@ -115,18 +103,18 @@ LL | fn invalid_deprecation_version() {} | ----------------------------------- the stability attribute annotates this item error[E0549]: deprecated attribute must be paired with either stable or unstable attribute - --> $DIR/stability-attribute-sanity.rs:72:1 + --> $DIR/stability-attribute-sanity.rs:66:1 | LL | #[deprecated(since = "a", note = "text")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0711]: feature `a` is declared stable since 1.0.0, but was previously declared stable since b - --> $DIR/stability-attribute-sanity.rs:67:1 + --> $DIR/stability-attribute-sanity.rs:61:1 | LL | #[stable(feature = "a", since = "1.0.0")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 20 previous errors +error: aborting due to 18 previous errors Some errors have detailed explanations: E0539, E0541, E0542, E0543, E0544, E0546, E0547, E0549, E0550. For more information about an error, try `rustc --explain E0539`. diff --git a/src/test/ui/stability-attribute/two_unstables.rs b/src/test/ui/stability-attribute/two_unstables.rs new file mode 100644 index 0000000000000..77feebfc1f71d --- /dev/null +++ b/src/test/ui/stability-attribute/two_unstables.rs @@ -0,0 +1,11 @@ +//check-pass + +#![unstable(feature = "module", issue = "none")] + +#![feature(staged_api)] + +#[unstable(feature = "a", issue = "none")] +#[unstable(feature = "b", issue = "none")] +pub struct Foo; + +fn main() {} diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 891531951c1a0..190e78ef95a24 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -354,7 +354,7 @@ fn check_terminator<'a, 'tcx>( fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool { tcx.is_const_fn(def_id) && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| { - if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level { + if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level { // Checking MSRV is manually necessary because `rustc` has no such concept. This entire // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`. // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. From 013ac1a8458ddae6b0ade98e8a3311baa0c69210 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 16 Mar 2022 20:18:03 -0700 Subject: [PATCH 2/2] Remove spans from StabilityLevel, so it can be decoded in ProcMacroData --- compiler/rustc_attr/src/builtin.rs | 100 ++++++++++-------- .../src/transform/check_consts/check.rs | 6 +- compiler/rustc_expand/src/base.rs | 15 ++- compiler/rustc_passes/src/stability.rs | 43 ++++---- 4 files changed, 86 insertions(+), 78 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 2e1c6358c5dd4..929790a1328b1 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -114,18 +114,7 @@ pub struct ConstStability { #[derive(HashStable_Generic)] pub enum StabilityLevel { Unstable { unstables: Vec, is_soft: bool }, - Stable { feature: Symbol, since: Symbol, span: Span }, -} - -impl StabilityLevel { - pub fn spans(&self) -> Vec { - match self { - StabilityLevel::Stable { span, .. } => vec![*span], - StabilityLevel::Unstable { unstables, .. } => { - unstables.iter().map(|u| u.span).collect() - } - } - } + Stable { feature: Symbol, since: Symbol }, } /// An instance of the `#[unstable]` attribute @@ -135,7 +124,6 @@ pub struct Unstability { pub feature: Symbol, pub issue: Option, pub reason: Option, - pub span: Span, } impl StabilityLevel { @@ -162,7 +150,7 @@ pub fn find_stability( sess: &Session, attrs: &[Attribute], item_sp: Span, -) -> (Option, Option) { +) -> (Option<(Stability, Vec)>, Option<(ConstStability, Vec)>) { find_stability_generic(sess, attrs.iter(), item_sp) } @@ -170,14 +158,14 @@ fn find_stability_generic<'a, I>( sess: &Session, attrs_iter: I, item_sp: Span, -) -> (Option, Option) +) -> (Option<(Stability, Vec)>, Option<(ConstStability, Vec)>) where I: Iterator, { use StabilityLevel::*; - let mut stab: Option = None; - let mut const_stab: Option = None; + let mut stab: Option<(Stability, Vec)> = None; + let mut const_stab: Option<(ConstStability, Vec)> = None; let mut promotable = false; let diagnostic = &sess.parse_sess.span_diagnostic; @@ -319,24 +307,33 @@ where ); continue; } - let unstability = - Unstability { feature, issue: issue_num, reason, span: attr.span }; + let unstability = Unstability { feature, issue: issue_num, reason }; if sym::unstable == meta_name { match &mut stab { stab @ None => { - *stab = Some(Stability { - level: StabilityLevel::Unstable { - unstables: vec![unstability], - is_soft, + *stab = Some(( + Stability { + level: StabilityLevel::Unstable { + unstables: vec![unstability], + is_soft, + }, }, - }) + vec![attr.span], + )) } // Merge multiple unstability attributes into one - Some(Stability { - level: - StabilityLevel::Unstable { unstables, is_soft: old_is_soft }, - }) => { + Some(( + Stability { + level: + StabilityLevel::Unstable { + unstables, + is_soft: old_is_soft, + }, + }, + spans, + )) => { unstables.push(unstability); + spans.push(attr.span); // FIXME(compiler-errors): Do we want this behavior: is_soft iff all are is_soft? *old_is_soft &= is_soft; } @@ -351,28 +348,34 @@ where } else { match &mut const_stab { const_stab @ None => { - *const_stab = Some(ConstStability { - level: StabilityLevel::Unstable { - unstables: vec![unstability], - is_soft, + *const_stab = Some(( + ConstStability { + level: StabilityLevel::Unstable { + unstables: vec![unstability], + is_soft, + }, + promotable: false, }, - promotable: false, - }) + vec![attr.span], + )) } - Some(ConstStability { - level: - StabilityLevel::Unstable { - unstables: unstability, - is_soft: old_is_soft, - }, - .. - }) => { + Some(( + ConstStability { + level: + StabilityLevel::Unstable { + unstables: unstability, + is_soft: old_is_soft, + }, + .. + }, + spans, + )) => { unstability.push(Unstability { feature, issue: issue_num, reason, - span: attr.span, }); + spans.push(attr.span); *old_is_soft &= is_soft; } _ => { @@ -453,11 +456,14 @@ where match (feature, since) { (Some(feature), Some(since)) => { - let level = Stable { since, feature, span: attr.span }; + let level = Stable { since, feature }; if sym::stable == meta_name { - stab = Some(Stability { level }); + stab = Some((Stability { level }, vec![attr.span])); } else { - const_stab = Some(ConstStability { level, promotable: false }); + const_stab = Some(( + ConstStability { level, promotable: false }, + vec![attr.span], + )); } } (None, _) => { @@ -477,7 +483,7 @@ where // Merge the const-unstable info into the stability info if promotable { - if let Some(stab) = &mut const_stab { + if let Some((stab, _)) = &mut const_stab { stab.promotable = promotable; } else { struct_span_err!( diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 3e2b74cd97d27..5f2a069516661 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -927,7 +927,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // Calling an unstable function *always* requires that the corresponding gate // be enabled, even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`. - if !tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == feature) + if !tcx + .features() + .declared_lib_features + .iter() + .any(|&(sym, _)| sym == feature) { bad_gates.push(feature); continue; diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index cae6c09533cfa..cdcc80b7c6a93 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -776,17 +776,16 @@ impl SyntaxExtension { }) .unwrap_or_else(|| (None, helper_attrs)); let (stability, const_stability) = attr::find_stability(&sess, attrs, span); - if let Some(stab) = const_stability { - let spans = stab.level.spans(); - let mut diag = sess - .parse_sess - .span_diagnostic - .struct_span_err(spans.clone(), "macros cannot have const stability attributes"); + if let Some((_, const_spans)) = const_stability { + let mut diag = sess.parse_sess.span_diagnostic.struct_span_err( + const_spans.clone(), + "macros cannot have const stability attributes", + ); diag.span_label( sess.source_map().guess_head_span(span), "const stability attribute affects this macro", ); - for sp in spans { + for sp in const_spans { diag.span_label(sp, "invalid const stability attribute"); } diag.emit(); @@ -799,7 +798,7 @@ impl SyntaxExtension { .then(|| allow_internal_unstable.into()), allow_internal_unsafe: sess.contains_name(attrs, sym::allow_internal_unsafe), local_inner_macros, - stability, + stability: stability.map(|(stab, _)| stab), deprecation: attr::find_deprecation(&sess, attrs).map(|(d, _)| d), helper_attrs, edition, diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 0593678da8885..ae4659e369057 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -162,15 +162,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp); - // Intern stability - let stab = stab.map(|stab| self.tcx.mk_stability(stab)); - - // Intern const_stability - let const_stab = const_stab.map(|const_stab| self.tcx.mk_const_stability(const_stab)); // If the current node is a function, has const stability attributes and if it doesn not have an intrinsic ABI, // check if the function/method is const or the parent impl block is const - if let (Some(const_stab), Some(fn_sig)) = (const_stab, fn_sig) { + if let (Some((_, const_spans)), Some(fn_sig)) = (&const_stab, fn_sig) { if fn_sig.header.abi != Abi::RustIntrinsic && fn_sig.header.abi != Abi::PlatformIntrinsic && !fn_sig.header.is_const() @@ -178,15 +173,17 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if !self.in_trait_impl || (self.in_trait_impl && !self.tcx.is_const_fn_raw(def_id.to_def_id())) { - missing_const_err(&self.tcx.sess, fn_sig.span, const_stab.level.spans()); + missing_const_err(&self.tcx.sess, fn_sig.span, const_spans); } } } // `impl const Trait for Type` items forward their const stability to their // immediate children. - if let Some(const_stab) = const_stab { + let const_stab = if let Some((const_stab, _)) = const_stab { + let const_stab = self.tcx.mk_const_stability(const_stab); self.index.const_stab_map.insert(def_id, const_stab); + Some(const_stab) } else { debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab); if let Some(parent) = self.parent_const_stab { @@ -194,7 +191,8 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { self.index.const_stab_map.insert(def_id, parent); } } - } + None + }; if let Some((rustc_attr::Deprecation { is_since_rustc_version: true, .. }, span)) = &depr { if stab.is_none() { @@ -209,8 +207,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } - let stab = stab.map(|stab| { - let spans = stab.level.spans(); + let stab = stab.map(|(stab, stab_spans)| { // Error if prohibited, or can't inherit anything from a container. if kind == AnnotationKind::Prohibited || (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated) @@ -218,8 +215,8 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { let mut diag = self .tcx .sess - .struct_span_err(spans.clone(), "this stability annotation is useless"); - for sp in &spans { + .struct_span_err(stab_spans.clone(), "this stability annotation is useless"); + for sp in &stab_spans { diag.span_label(*sp, "useless stability annotation"); } diag.span_label(item_sp, "the stability attribute annotates this item").emit(); @@ -229,9 +226,11 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // Check if deprecated_since < stable_since. If it is, // this is *almost surely* an accident. - if let (&Some(dep_since), &attr::Stable { since: stab_since, span, .. }) = + if let (&Some(dep_since), &attr::Stable { since: stab_since, .. }) = (&depr.as_ref().and_then(|(d, _)| d.since), &stab.level) { + let span = *stab_spans.first().expect("expected one span with a stable attribute"); + // Explicit version of iter::order::lt to handle parse errors properly for (dep_v, stab_v) in iter::zip(dep_since.as_str().split('.'), stab_since.as_str().split('.')) @@ -252,7 +251,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { self.tcx .sess .struct_span_err( - spans, + stab_spans, "an API can't be stabilized after it is deprecated", ) .span_label(span, "invalid version") @@ -285,6 +284,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } + let stab = self.tcx.mk_stability(stab); self.index.stab_map.insert(def_id, stab); stab }); @@ -659,7 +659,6 @@ fn stability_index<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> Index<'tcx> { reason: Some(Symbol::intern(reason)), issue: NonZeroU32::new(27812), feature: sym::rustc_private, - span: rustc_span::DUMMY_SP, }], is_soft: false, }, @@ -740,18 +739,18 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // it will have no effect. // See: https://github.com/rust-lang/rust/issues/55436 let attrs = self.tcx.hir().attrs(item.hir_id()); - if let (Some(Stability { level: attr::Unstable { unstables, .. }, .. }), _) = - attr::find_stability(&self.tcx.sess, attrs, item.span) + if let (Some((stab, spans)), _) = + attr::find_stability(&self.tcx.sess, attrs, item.span) && stab.level.is_unstable() { let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; c.visit_ty(self_ty); c.visit_trait_ref(t); if c.fully_stable { - for u in unstables { + for span in spans { self.tcx.struct_span_lint_hir( INEFFECTIVE_UNSTABLE_TRAIT_IMPL, item.hir_id(), - u.span, + span, |lint| { lint .build("an `#[unstable]` annotation here has no effect") @@ -961,14 +960,14 @@ fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) { .emit(); } -fn missing_const_err(session: &Session, fn_sig_span: Span, const_spans: Vec) { +fn missing_const_err(session: &Session, fn_sig_span: Span, const_spans: &[Span]) { const ERROR_MSG: &'static str = "attributes `#[rustc_const_unstable]` \ and `#[rustc_const_stable]` require \ the function or method to be `const`"; let mut diag = session.struct_span_err(fn_sig_span, ERROR_MSG); for sp in const_spans { - diag.span_label(sp, "attribute specified here"); + diag.span_label(*sp, "attribute specified here"); } diag.span_help(fn_sig_span, "make the function or method const").emit(); }