middle: add implies_by
to #[unstable]
If part of a feature is stabilized and a new feature is added for the remaining parts, then the `implied_by` attribute can be used to indicate which now-stable feature previously contained a item. If the now-stable feature is still active (if the user has only just updated rustc, for example) then there will not be an stability error for uses of the item from the implied feature. Signed-off-by: David Wood <david.wood@huawei.com>
This commit is contained in:
parent
a1d5af24ec
commit
224aec213d
@ -142,6 +142,26 @@ pub enum StabilityLevel {
|
|||||||
/// Relevant `rust-lang/rust` issue.
|
/// Relevant `rust-lang/rust` issue.
|
||||||
issue: Option<NonZeroU32>,
|
issue: Option<NonZeroU32>,
|
||||||
is_soft: bool,
|
is_soft: bool,
|
||||||
|
/// If part of a feature is stabilized and a new feature is added for the remaining parts,
|
||||||
|
/// then the `implied_by` attribute is used to indicate which now-stable feature previously
|
||||||
|
/// contained a item.
|
||||||
|
///
|
||||||
|
/// ```pseudo-Rust
|
||||||
|
/// #[unstable(feature = "foo", issue = "...")]
|
||||||
|
/// fn foo() {}
|
||||||
|
/// #[unstable(feature = "foo", issue = "...")]
|
||||||
|
/// fn foobar() {}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ...becomes...
|
||||||
|
///
|
||||||
|
/// ```pseudo-Rust
|
||||||
|
/// #[stable(feature = "foo", since = "1.XX.X")]
|
||||||
|
/// fn foo() {}
|
||||||
|
/// #[unstable(feature = "foobar", issue = "...", implied_by = "foo")]
|
||||||
|
/// fn foobar() {}
|
||||||
|
/// ```
|
||||||
|
implied_by: Option<Symbol>,
|
||||||
},
|
},
|
||||||
/// `#[stable]`
|
/// `#[stable]`
|
||||||
Stable {
|
Stable {
|
||||||
@ -256,6 +276,7 @@ fn find_stability_generic<'a, I>(
|
|||||||
let mut issue = None;
|
let mut issue = None;
|
||||||
let mut issue_num = None;
|
let mut issue_num = None;
|
||||||
let mut is_soft = false;
|
let mut is_soft = false;
|
||||||
|
let mut implied_by = None;
|
||||||
for meta in metas {
|
for meta in metas {
|
||||||
let Some(mi) = meta.meta_item() else {
|
let Some(mi) = meta.meta_item() else {
|
||||||
handle_errors(
|
handle_errors(
|
||||||
@ -321,6 +342,11 @@ fn find_stability_generic<'a, I>(
|
|||||||
}
|
}
|
||||||
is_soft = true;
|
is_soft = true;
|
||||||
}
|
}
|
||||||
|
sym::implied_by => {
|
||||||
|
if !get(mi, &mut implied_by) {
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
handle_errors(
|
handle_errors(
|
||||||
&sess.parse_sess,
|
&sess.parse_sess,
|
||||||
@ -345,7 +371,7 @@ fn find_stability_generic<'a, I>(
|
|||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let level = Unstable { reason, issue: issue_num, is_soft };
|
let level = Unstable { reason, issue: issue_num, is_soft, implied_by };
|
||||||
if sym::unstable == meta_name {
|
if sym::unstable == meta_name {
|
||||||
stab = Some((Stability { level, feature }, attr.span));
|
stab = Some((Stability { level, feature }, attr.span));
|
||||||
} else {
|
} else {
|
||||||
|
@ -423,7 +423,9 @@ pub fn eval_stability_allow_unstable(
|
|||||||
|
|
||||||
match stability {
|
match stability {
|
||||||
Some(Stability {
|
Some(Stability {
|
||||||
level: attr::Unstable { reason, issue, is_soft }, feature, ..
|
level: attr::Unstable { reason, issue, is_soft, implied_by },
|
||||||
|
feature,
|
||||||
|
..
|
||||||
}) => {
|
}) => {
|
||||||
if span.allows_unstable(feature) {
|
if span.allows_unstable(feature) {
|
||||||
debug!("stability: skipping span={:?} since it is internal", span);
|
debug!("stability: skipping span={:?} since it is internal", span);
|
||||||
@ -433,6 +435,13 @@ pub fn eval_stability_allow_unstable(
|
|||||||
return EvalResult::Allow;
|
return EvalResult::Allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this item was previously part of a now-stabilized feature which is still
|
||||||
|
// active (i.e. the user hasn't removed the attribute for the stabilized feature
|
||||||
|
// yet) then allow use of this item.
|
||||||
|
if let Some(implied_by) = implied_by && self.features().active(implied_by) {
|
||||||
|
return EvalResult::Allow;
|
||||||
|
}
|
||||||
|
|
||||||
// When we're compiling the compiler itself we may pull in
|
// When we're compiling the compiler itself we may pull in
|
||||||
// crates from crates.io, but those crates may depend on other
|
// crates from crates.io, but those crates may depend on other
|
||||||
// crates also pulled in from crates.io. We want to ideally be
|
// crates also pulled in from crates.io. We want to ideally be
|
||||||
|
@ -637,6 +637,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index {
|
|||||||
reason: Some(Symbol::intern(reason)),
|
reason: Some(Symbol::intern(reason)),
|
||||||
issue: NonZeroU32::new(27812),
|
issue: NonZeroU32::new(27812),
|
||||||
is_soft: false,
|
is_soft: false,
|
||||||
|
implied_by: None,
|
||||||
},
|
},
|
||||||
feature: sym::rustc_private,
|
feature: sym::rustc_private,
|
||||||
};
|
};
|
||||||
|
@ -796,9 +796,16 @@ fn check_stability_and_deprecation(
|
|||||||
) {
|
) {
|
||||||
let span = path.span;
|
let span = path.span;
|
||||||
if let Some(stability) = &ext.stability {
|
if let Some(stability) = &ext.stability {
|
||||||
if let StabilityLevel::Unstable { reason, issue, is_soft } = stability.level {
|
if let StabilityLevel::Unstable { reason, issue, is_soft, implied_by } = stability.level
|
||||||
|
{
|
||||||
let feature = stability.feature;
|
let feature = stability.feature;
|
||||||
if !self.active_features.contains(&feature) && !span.allows_unstable(feature) {
|
|
||||||
|
let is_allowed = |feature| {
|
||||||
|
self.active_features.contains(&feature) || span.allows_unstable(feature)
|
||||||
|
};
|
||||||
|
let allowed_by_implication =
|
||||||
|
implied_by.map(|feature| is_allowed(feature)).unwrap_or(false);
|
||||||
|
if !is_allowed(feature) && !allowed_by_implication {
|
||||||
let lint_buffer = &mut self.lint_buffer;
|
let lint_buffer = &mut self.lint_buffer;
|
||||||
let soft_handler =
|
let soft_handler =
|
||||||
|lint, span, msg: &_| lint_buffer.buffer_lint(lint, node_id, span, msg);
|
|lint, span, msg: &_| lint_buffer.buffer_lint(lint, node_id, span, msg);
|
||||||
|
@ -800,6 +800,7 @@
|
|||||||
impl_lint_pass,
|
impl_lint_pass,
|
||||||
impl_macros,
|
impl_macros,
|
||||||
impl_trait_in_bindings,
|
impl_trait_in_bindings,
|
||||||
|
implied_by,
|
||||||
import,
|
import,
|
||||||
import_shadowing,
|
import_shadowing,
|
||||||
imported_main,
|
imported_main,
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
#![feature(staged_api)]
|
||||||
|
#![stable(feature = "stability_attribute_implies", since = "1.0.0")]
|
||||||
|
|
||||||
|
#[stable(feature = "foo", since = "1.62.0")]
|
||||||
|
pub fn foo() {}
|
||||||
|
|
||||||
|
#[unstable(feature = "foobar", issue = "1", implied_by = "foo")]
|
||||||
|
pub fn foobar() {}
|
@ -0,0 +1,13 @@
|
|||||||
|
// aux-build:stability-attribute-implies.rs
|
||||||
|
|
||||||
|
// Tests that despite the `foobar` feature being implied by now-stable feature `foo`, if `foobar`
|
||||||
|
// isn't allowed in this crate then an error will be emitted.
|
||||||
|
|
||||||
|
extern crate stability_attribute_implies;
|
||||||
|
use stability_attribute_implies::{foo, foobar};
|
||||||
|
//~^ ERROR use of unstable library feature 'foobar'
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo(); // no error - stable
|
||||||
|
foobar(); //~ ERROR use of unstable library feature 'foobar'
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
error[E0658]: use of unstable library feature 'foobar'
|
||||||
|
--> $DIR/stability-attribute-implies-no-feature.rs:7:40
|
||||||
|
|
|
||||||
|
LL | use stability_attribute_implies::{foo, foobar};
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #1 <https://github.com/rust-lang/rust/issues/1> for more information
|
||||||
|
= help: add `#![feature(foobar)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error[E0658]: use of unstable library feature 'foobar'
|
||||||
|
--> $DIR/stability-attribute-implies-no-feature.rs:12:5
|
||||||
|
|
|
||||||
|
LL | foobar();
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #1 <https://github.com/rust-lang/rust/issues/1> for more information
|
||||||
|
= help: add `#![feature(foobar)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0658`.
|
@ -0,0 +1,15 @@
|
|||||||
|
// aux-build:stability-attribute-implies.rs
|
||||||
|
#![deny(stable_features)]
|
||||||
|
#![feature(foo)]
|
||||||
|
//~^ ERROR the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable
|
||||||
|
|
||||||
|
// Tests that the use of `implied_by` in the `#[unstable]` attribute results in a diagnostic
|
||||||
|
// mentioning partial stabilization, and that given the implied unstable feature is unused (there
|
||||||
|
// is no `foobar` call), that the compiler suggests removing the flag.
|
||||||
|
|
||||||
|
extern crate stability_attribute_implies;
|
||||||
|
use stability_attribute_implies::foo;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo();
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
error: the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable
|
||||||
|
--> $DIR/stability-attribute-implies-using-stable.rs:3:12
|
||||||
|
|
|
||||||
|
LL | #![feature(foo)]
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/stability-attribute-implies-using-stable.rs:2:9
|
||||||
|
|
|
||||||
|
LL | #![deny(stable_features)]
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
@ -0,0 +1,17 @@
|
|||||||
|
// aux-build:stability-attribute-implies.rs
|
||||||
|
#![deny(stable_features)]
|
||||||
|
#![feature(foo)]
|
||||||
|
//~^ ERROR the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable
|
||||||
|
|
||||||
|
// Tests that the use of `implied_by` in the `#[unstable]` attribute results in a diagnostic
|
||||||
|
// mentioning partial stabilization and that given the implied unstable feature is used (there is a
|
||||||
|
// `foobar` call), that the compiler suggests changing to that feature and doesn't error about its
|
||||||
|
// use.
|
||||||
|
|
||||||
|
extern crate stability_attribute_implies;
|
||||||
|
use stability_attribute_implies::{foo, foobar};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo();
|
||||||
|
foobar(); // no error!
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
error: the feature `foo` has been stable since 1.62.0 and no longer requires an attribute to enable
|
||||||
|
--> $DIR/stability-attribute-implies-using-unstable.rs:3:12
|
||||||
|
|
|
||||||
|
LL | #![feature(foo)]
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/stability-attribute-implies-using-unstable.rs:2:9
|
||||||
|
|
|
||||||
|
LL | #![deny(stable_features)]
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
Loading…
Reference in New Issue
Block a user