allowed_through_unstable_modules: support showing a deprecation message when the unstable module name is used
This commit is contained in:
parent
561a097b65
commit
cf0ab86251
12 changed files with 146 additions and 59 deletions
|
@ -723,6 +723,8 @@ impl MetaItemLit {
|
||||||
pub trait AttributeExt: Debug {
|
pub trait AttributeExt: Debug {
|
||||||
fn id(&self) -> AttrId;
|
fn id(&self) -> AttrId;
|
||||||
|
|
||||||
|
/// For a single-segment attribute (i.e., `#[attr]` and not `#[path::atrr]`),
|
||||||
|
/// return the name of the attribute, else return the empty identifier.
|
||||||
fn name_or_empty(&self) -> Symbol {
|
fn name_or_empty(&self) -> Symbol {
|
||||||
self.ident().unwrap_or_else(Ident::empty).name
|
self.ident().unwrap_or_else(Ident::empty).name
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,6 +101,16 @@ impl PartialConstStability {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
||||||
|
#[derive(HashStable_Generic)]
|
||||||
|
pub enum AllowedThroughUnstableModules {
|
||||||
|
/// This does not get a deprecation warning. We still generally would prefer people to use the
|
||||||
|
/// fully stable path, and a warning will likely be emitted in the future.
|
||||||
|
WithoutDeprecation,
|
||||||
|
/// Emit the given deprecation warning.
|
||||||
|
WithDeprecation(Symbol),
|
||||||
|
}
|
||||||
|
|
||||||
/// The available stability levels.
|
/// The available stability levels.
|
||||||
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
||||||
#[derive(HashStable_Generic)]
|
#[derive(HashStable_Generic)]
|
||||||
|
@ -137,9 +147,8 @@ pub enum StabilityLevel {
|
||||||
Stable {
|
Stable {
|
||||||
/// Rust release which stabilized this feature.
|
/// Rust release which stabilized this feature.
|
||||||
since: StableSince,
|
since: StableSince,
|
||||||
/// Is this item allowed to be referred to on stable, despite being contained in unstable
|
/// This is `Some` if this item allowed to be referred to on stable via unstable modules.
|
||||||
/// modules?
|
allowed_through_unstable_modules: Option<AllowedThroughUnstableModules>,
|
||||||
allowed_through_unstable_modules: bool,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ use rustc_ast::MetaItem;
|
||||||
use rustc_ast::attr::AttributeExt;
|
use rustc_ast::attr::AttributeExt;
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
use rustc_attr_data_structures::{
|
use rustc_attr_data_structures::{
|
||||||
ConstStability, DefaultBodyStability, Stability, StabilityLevel, StableSince, UnstableReason,
|
AllowedThroughUnstableModules, ConstStability, DefaultBodyStability, Stability, StabilityLevel,
|
||||||
VERSION_PLACEHOLDER,
|
StableSince, UnstableReason, VERSION_PLACEHOLDER,
|
||||||
};
|
};
|
||||||
use rustc_errors::ErrorGuaranteed;
|
use rustc_errors::ErrorGuaranteed;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
|
@ -24,11 +24,16 @@ pub fn find_stability(
|
||||||
item_sp: Span,
|
item_sp: Span,
|
||||||
) -> Option<(Stability, Span)> {
|
) -> Option<(Stability, Span)> {
|
||||||
let mut stab: Option<(Stability, Span)> = None;
|
let mut stab: Option<(Stability, Span)> = None;
|
||||||
let mut allowed_through_unstable_modules = false;
|
let mut allowed_through_unstable_modules = None;
|
||||||
|
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
match attr.name_or_empty() {
|
match attr.name_or_empty() {
|
||||||
sym::rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true,
|
sym::rustc_allowed_through_unstable_modules => {
|
||||||
|
allowed_through_unstable_modules = Some(match attr.value_str() {
|
||||||
|
Some(msg) => AllowedThroughUnstableModules::WithDeprecation(msg),
|
||||||
|
None => AllowedThroughUnstableModules::WithoutDeprecation,
|
||||||
|
})
|
||||||
|
}
|
||||||
sym::unstable => {
|
sym::unstable => {
|
||||||
if stab.is_some() {
|
if stab.is_some() {
|
||||||
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
|
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
|
||||||
|
@ -56,15 +61,15 @@ pub fn find_stability(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if allowed_through_unstable_modules {
|
if let Some(allowed_through_unstable_modules) = allowed_through_unstable_modules {
|
||||||
match &mut stab {
|
match &mut stab {
|
||||||
Some((
|
Some((
|
||||||
Stability {
|
Stability {
|
||||||
level: StabilityLevel::Stable { allowed_through_unstable_modules, .. },
|
level: StabilityLevel::Stable { allowed_through_unstable_modules: in_stab, .. },
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
_,
|
_,
|
||||||
)) => *allowed_through_unstable_modules = true,
|
)) => *in_stab = Some(allowed_through_unstable_modules),
|
||||||
_ => {
|
_ => {
|
||||||
sess.dcx()
|
sess.dcx()
|
||||||
.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
|
.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
|
||||||
|
@ -283,7 +288,7 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
|
||||||
|
|
||||||
match feature {
|
match feature {
|
||||||
Ok(feature) => {
|
Ok(feature) => {
|
||||||
let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false };
|
let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: None };
|
||||||
Some((feature, level))
|
Some((feature, level))
|
||||||
}
|
}
|
||||||
Err(ErrorGuaranteed { .. }) => None,
|
Err(ErrorGuaranteed { .. }) => None,
|
||||||
|
|
|
@ -623,7 +623,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||||
EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint",
|
EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint",
|
||||||
),
|
),
|
||||||
rustc_attr!(
|
rustc_attr!(
|
||||||
rustc_allowed_through_unstable_modules, Normal, template!(Word),
|
rustc_allowed_through_unstable_modules, Normal, template!(Word, NameValueStr: "deprecation message"),
|
||||||
WarnFollowing, EncodeCrossCrate::No,
|
WarnFollowing, EncodeCrossCrate::No,
|
||||||
"rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
|
"rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
|
||||||
through unstable paths"
|
through unstable paths"
|
||||||
|
|
|
@ -5,8 +5,8 @@ use std::mem::replace;
|
||||||
use std::num::NonZero;
|
use std::num::NonZero;
|
||||||
|
|
||||||
use rustc_attr_parsing::{
|
use rustc_attr_parsing::{
|
||||||
self as attr, ConstStability, DeprecatedSince, Stability, StabilityLevel, StableSince,
|
self as attr, AllowedThroughUnstableModules, ConstStability, DeprecatedSince, Stability,
|
||||||
UnstableReason, VERSION_PLACEHOLDER,
|
StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER,
|
||||||
};
|
};
|
||||||
use rustc_data_structures::fx::FxIndexMap;
|
use rustc_data_structures::fx::FxIndexMap;
|
||||||
use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
|
use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
|
||||||
|
@ -20,11 +20,16 @@ use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant};
|
||||||
use rustc_middle::hir::nested_filter;
|
use rustc_middle::hir::nested_filter;
|
||||||
use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures};
|
use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures};
|
||||||
use rustc_middle::middle::privacy::EffectiveVisibilities;
|
use rustc_middle::middle::privacy::EffectiveVisibilities;
|
||||||
use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index};
|
use rustc_middle::middle::stability::{
|
||||||
|
AllowUnstable, Deprecated, DeprecationEntry, EvalResult, Index,
|
||||||
|
};
|
||||||
use rustc_middle::query::Providers;
|
use rustc_middle::query::Providers;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
|
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||||
use rustc_session::lint;
|
use rustc_session::lint;
|
||||||
use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED};
|
use rustc_session::lint::builtin::{
|
||||||
|
DEPRECATED, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED,
|
||||||
|
};
|
||||||
use rustc_span::{Span, Symbol, sym};
|
use rustc_span::{Span, Symbol, sym};
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
|
@ -844,16 +849,17 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let is_allowed_through_unstable_modules = |def_id| {
|
if item_is_allowed {
|
||||||
self.tcx.lookup_stability(def_id).is_some_and(|stab| match stab.level {
|
// The item itself is allowed; check whether the path there is also allowed.
|
||||||
|
let is_allowed_through_unstable_modules: Option<AllowedThroughUnstableModules> =
|
||||||
|
self.tcx.lookup_stability(def_id).and_then(|stab| match stab.level {
|
||||||
StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
|
StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
|
||||||
allowed_through_unstable_modules
|
allowed_through_unstable_modules
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => None,
|
||||||
})
|
});
|
||||||
};
|
|
||||||
|
|
||||||
if item_is_allowed && !is_allowed_through_unstable_modules(def_id) {
|
if is_allowed_through_unstable_modules.is_none() {
|
||||||
// Check parent modules stability as well if the item the path refers to is itself
|
// Check parent modules stability as well if the item the path refers to is itself
|
||||||
// stable. We only emit warnings for unstable path segments if the item is stable
|
// stable. We only emit warnings for unstable path segments if the item is stable
|
||||||
// or allowed because stability is often inherited, so the most common case is that
|
// or allowed because stability is often inherited, so the most common case is that
|
||||||
|
@ -882,6 +888,58 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if let Some(AllowedThroughUnstableModules::WithDeprecation(deprecation)) =
|
||||||
|
is_allowed_through_unstable_modules
|
||||||
|
{
|
||||||
|
// Similar to above, but we cannot use `check_stability_allow_unstable` as that would
|
||||||
|
// immediately show the stability error. We just want to know the result and disaplay
|
||||||
|
// our own kind of error.
|
||||||
|
let parents = path.segments.iter().rev().skip(1);
|
||||||
|
for path_segment in parents {
|
||||||
|
if let Some(def_id) = path_segment.res.opt_def_id() {
|
||||||
|
// use `None` for id to prevent deprecation check
|
||||||
|
let eval_result = self.tcx.eval_stability_allow_unstable(
|
||||||
|
def_id,
|
||||||
|
None,
|
||||||
|
path.span,
|
||||||
|
None,
|
||||||
|
if is_unstable_reexport(self.tcx, id) {
|
||||||
|
AllowUnstable::Yes
|
||||||
|
} else {
|
||||||
|
AllowUnstable::No
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let is_allowed = matches!(eval_result, EvalResult::Allow);
|
||||||
|
if !is_allowed {
|
||||||
|
// Calculating message for lint involves calling `self.def_path_str`,
|
||||||
|
// which will by default invoke the expensive `visible_parent_map` query.
|
||||||
|
// Skip all that work if the lint is allowed anyway.
|
||||||
|
if self.tcx.lint_level_at_node(DEPRECATED, id).0
|
||||||
|
== lint::Level::Allow
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Show a deprecation message.
|
||||||
|
let def_path =
|
||||||
|
with_no_trimmed_paths!(self.tcx.def_path_str(def_id));
|
||||||
|
let def_kind = self.tcx.def_descr(def_id);
|
||||||
|
let diag = Deprecated {
|
||||||
|
sub: None,
|
||||||
|
kind: def_kind.to_owned(),
|
||||||
|
path: def_path,
|
||||||
|
note: Some(deprecation),
|
||||||
|
since_kind: lint::DeprecatedSinceKind::InEffect,
|
||||||
|
};
|
||||||
|
self.tcx.emit_node_span_lint(
|
||||||
|
DEPRECATED,
|
||||||
|
id,
|
||||||
|
method_span.unwrap_or(path.span),
|
||||||
|
diag,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -406,7 +406,7 @@ impl Item {
|
||||||
// were never supposed to work at all.
|
// were never supposed to work at all.
|
||||||
let stab = self.stability(tcx)?;
|
let stab = self.stability(tcx)?;
|
||||||
if let rustc_attr_parsing::StabilityLevel::Stable {
|
if let rustc_attr_parsing::StabilityLevel::Stable {
|
||||||
allowed_through_unstable_modules: true,
|
allowed_through_unstable_modules: Some(_),
|
||||||
..
|
..
|
||||||
} = stab.level
|
} = stab.level
|
||||||
{
|
{
|
||||||
|
|
|
@ -316,7 +316,7 @@ impl DocFolder for CacheBuilder<'_, '_> {
|
||||||
|
|
||||||
let skip_because_unstable = matches!(
|
let skip_because_unstable = matches!(
|
||||||
item.stability.map(|stab| stab.level),
|
item.stability.map(|stab| stab.level),
|
||||||
Some(StabilityLevel::Stable { allowed_through_unstable_modules: true, .. })
|
Some(StabilityLevel::Stable { allowed_through_unstable_modules: Some(_), .. })
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!self.cache.stripped_mod && !skip_because_unstable) || self.is_json_output {
|
if (!self.cache.stripped_mod && !skip_because_unstable) || self.is_json_output {
|
||||||
|
|
|
@ -119,7 +119,7 @@ fn merge_stability(
|
||||||
parent_stability: Option<Stability>,
|
parent_stability: Option<Stability>,
|
||||||
) -> Option<Stability> {
|
) -> Option<Stability> {
|
||||||
if let Some(own_stab) = own_stability
|
if let Some(own_stab) = own_stability
|
||||||
&& let StabilityLevel::Stable { since: own_since, allowed_through_unstable_modules: false } =
|
&& let StabilityLevel::Stable { since: own_since, allowed_through_unstable_modules: None } =
|
||||||
own_stab.level
|
own_stab.level
|
||||||
&& let Some(parent_stab) = parent_stability
|
&& let Some(parent_stab) = parent_stability
|
||||||
&& (parent_stab.is_unstable()
|
&& (parent_stab.is_unstable()
|
||||||
|
@ -127,12 +127,12 @@ fn merge_stability(
|
||||||
{
|
{
|
||||||
parent_stability
|
parent_stability
|
||||||
} else if let Some(mut own_stab) = own_stability
|
} else if let Some(mut own_stab) = own_stability
|
||||||
&& let StabilityLevel::Stable { since, allowed_through_unstable_modules: true } =
|
&& let StabilityLevel::Stable { since, allowed_through_unstable_modules: Some(_) } =
|
||||||
own_stab.level
|
own_stab.level
|
||||||
&& parent_stability.is_some_and(|stab| stab.is_stable())
|
&& parent_stability.is_some_and(|stab| stab.is_stable())
|
||||||
{
|
{
|
||||||
// this property does not apply transitively through re-exports
|
// this property does not apply transitively through re-exports
|
||||||
own_stab.level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false };
|
own_stab.level = StabilityLevel::Stable { since, allowed_through_unstable_modules: None };
|
||||||
Some(own_stab)
|
Some(own_stab)
|
||||||
} else {
|
} else {
|
||||||
own_stability
|
own_stability
|
||||||
|
|
|
@ -180,7 +180,7 @@ fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: &Msrv) -> bool {
|
||||||
if let Some(stability) = cx.tcx.lookup_stability(def_id)
|
if let Some(stability) = cx.tcx.lookup_stability(def_id)
|
||||||
&& let StabilityLevel::Stable {
|
&& let StabilityLevel::Stable {
|
||||||
since,
|
since,
|
||||||
allowed_through_unstable_modules: false,
|
allowed_through_unstable_modules: None,
|
||||||
} = stability.level
|
} = stability.level
|
||||||
{
|
{
|
||||||
let stable = match since {
|
let stable = match since {
|
||||||
|
|
|
@ -6,4 +6,5 @@
|
||||||
extern crate allowed_through_unstable_core;
|
extern crate allowed_through_unstable_core;
|
||||||
|
|
||||||
use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstable;
|
use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstable;
|
||||||
|
use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstableWithDeprecation; //~WARN use of deprecated module `allowed_through_unstable_core::unstable_module`: use the new path instead
|
||||||
use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowedThroughUnstable; //~ ERROR use of unstable library feature `unstable_test_feature`
|
use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowedThroughUnstable; //~ ERROR use of unstable library feature `unstable_test_feature`
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
|
warning: use of deprecated module `allowed_through_unstable_core::unstable_module`: use the new path instead
|
||||||
|
--> $DIR/allowed-through-unstable.rs:9:53
|
||||||
|
|
|
||||||
|
LL | use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstableWithDeprecation;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(deprecated)]` on by default
|
||||||
|
|
||||||
error[E0658]: use of unstable library feature `unstable_test_feature`
|
error[E0658]: use of unstable library feature `unstable_test_feature`
|
||||||
--> $DIR/allowed-through-unstable.rs:9:5
|
--> $DIR/allowed-through-unstable.rs:10:5
|
||||||
|
|
|
|
||||||
LL | use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowedThroughUnstable;
|
LL | use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowedThroughUnstable;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -8,6 +16,6 @@ LL | use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowe
|
||||||
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
|
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
|
||||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error; 1 warning emitted
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0658`.
|
For more information about this error, try `rustc --explain E0658`.
|
||||||
|
|
|
@ -9,6 +9,10 @@ pub mod unstable_module {
|
||||||
#[rustc_allowed_through_unstable_modules]
|
#[rustc_allowed_through_unstable_modules]
|
||||||
pub trait OldStableTraitAllowedThoughUnstable {}
|
pub trait OldStableTraitAllowedThoughUnstable {}
|
||||||
|
|
||||||
|
#[stable(feature = "stable_test_feature", since = "1.2.0")]
|
||||||
|
#[rustc_allowed_through_unstable_modules = "use the new path instead"]
|
||||||
|
pub trait OldStableTraitAllowedThoughUnstableWithDeprecation {}
|
||||||
|
|
||||||
#[stable(feature = "stable_test_feature", since = "1.2.0")]
|
#[stable(feature = "stable_test_feature", since = "1.2.0")]
|
||||||
pub trait NewStableTraitNotAllowedThroughUnstable {}
|
pub trait NewStableTraitNotAllowedThroughUnstable {}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue