Rollup merge of #97301 - semicoleon:unstable-reexport, r=petrochenkov
Allow unstable items to be re-exported unstably without requiring the feature be enabled Closes #94972 The diagnostic may need some work still, and I haven't added a test yet
This commit is contained in:
commit
2035b50d80
6 changed files with 174 additions and 11 deletions
|
@ -9,10 +9,9 @@ use rustc_attr::{self as attr, ConstStability, Deprecation, Stability};
|
|||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{Applicability, Diagnostic};
|
||||
use rustc_feature::GateIssue;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{self, HirId};
|
||||
use rustc_hir::{self as hir, HirId};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
|
||||
use rustc_session::lint::{BuiltinLintDiagnostics, Level, Lint, LintBuffer};
|
||||
|
@ -306,6 +305,14 @@ fn suggestion_for_allocator_api(
|
|||
None
|
||||
}
|
||||
|
||||
/// An override option for eval_stability.
|
||||
pub enum AllowUnstable {
|
||||
/// Don't emit an unstable error for the item
|
||||
Yes,
|
||||
/// Handle the item normally
|
||||
No,
|
||||
}
|
||||
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
/// Evaluates the stability of an item.
|
||||
///
|
||||
|
@ -322,6 +329,28 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
id: Option<HirId>,
|
||||
span: Span,
|
||||
method_span: Option<Span>,
|
||||
) -> EvalResult {
|
||||
self.eval_stability_allow_unstable(def_id, id, span, method_span, AllowUnstable::No)
|
||||
}
|
||||
|
||||
/// Evaluates the stability of an item.
|
||||
///
|
||||
/// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding
|
||||
/// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending
|
||||
/// unstable feature otherwise.
|
||||
///
|
||||
/// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been
|
||||
/// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to
|
||||
/// `id`.
|
||||
///
|
||||
/// Pass `AllowUnstable::Yes` to `allow_unstable` to force an unstable item to be allowed. Deprecation warnings will be emitted normally.
|
||||
pub fn eval_stability_allow_unstable(
|
||||
self,
|
||||
def_id: DefId,
|
||||
id: Option<HirId>,
|
||||
span: Span,
|
||||
method_span: Option<Span>,
|
||||
allow_unstable: AllowUnstable,
|
||||
) -> EvalResult {
|
||||
// Deprecated attributes apply in-crate and cross-crate.
|
||||
if let Some(id) = id {
|
||||
|
@ -419,6 +448,10 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
if matches!(allow_unstable, AllowUnstable::Yes) {
|
||||
return EvalResult::Allow;
|
||||
}
|
||||
|
||||
let suggestion = suggestion_for_allocator_api(self, def_id, span, feature);
|
||||
EvalResult::Deny { feature, reason, issue, suggestion, is_soft }
|
||||
}
|
||||
|
@ -445,11 +478,38 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
span: Span,
|
||||
method_span: Option<Span>,
|
||||
) {
|
||||
self.check_optional_stability(def_id, id, span, method_span, |span, def_id| {
|
||||
// The API could be uncallable for other reasons, for example when a private module
|
||||
// was referenced.
|
||||
self.sess.delay_span_bug(span, &format!("encountered unmarked API: {:?}", def_id));
|
||||
})
|
||||
self.check_stability_allow_unstable(def_id, id, span, method_span, AllowUnstable::No)
|
||||
}
|
||||
|
||||
/// Checks if an item is stable or error out.
|
||||
///
|
||||
/// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not
|
||||
/// exist, emits an error.
|
||||
///
|
||||
/// This function will also check if the item is deprecated.
|
||||
/// If so, and `id` is not `None`, a deprecated lint attached to `id` will be emitted.
|
||||
///
|
||||
/// Pass `AllowUnstable::Yes` to `allow_unstable` to force an unstable item to be allowed. Deprecation warnings will be emitted normally.
|
||||
pub fn check_stability_allow_unstable(
|
||||
self,
|
||||
def_id: DefId,
|
||||
id: Option<HirId>,
|
||||
span: Span,
|
||||
method_span: Option<Span>,
|
||||
allow_unstable: AllowUnstable,
|
||||
) {
|
||||
self.check_optional_stability(
|
||||
def_id,
|
||||
id,
|
||||
span,
|
||||
method_span,
|
||||
allow_unstable,
|
||||
|span, def_id| {
|
||||
// The API could be uncallable for other reasons, for example when a private module
|
||||
// was referenced.
|
||||
self.sess.delay_span_bug(span, &format!("encountered unmarked API: {:?}", def_id));
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Like `check_stability`, except that we permit items to have custom behaviour for
|
||||
|
@ -462,6 +522,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
id: Option<HirId>,
|
||||
span: Span,
|
||||
method_span: Option<Span>,
|
||||
allow_unstable: AllowUnstable,
|
||||
unmarked: impl FnOnce(Span, DefId),
|
||||
) {
|
||||
let soft_handler = |lint, span, msg: &_| {
|
||||
|
@ -469,7 +530,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
lint.build(msg).emit();
|
||||
})
|
||||
};
|
||||
match self.eval_stability(def_id, id, span, method_span) {
|
||||
match self.eval_stability_allow_unstable(def_id, id, span, method_span, allow_unstable) {
|
||||
EvalResult::Allow => {}
|
||||
EvalResult::Deny { feature, reason, issue, suggestion, is_soft } => report_unstable(
|
||||
self.sess,
|
||||
|
|
|
@ -9,10 +9,10 @@ use rustc_hir::def::{DefKind, Res};
|
|||
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
|
||||
use rustc_hir::hir_id::CRATE_HIR_ID;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{FieldDef, Generics, HirId, Item, TraitRef, Ty, TyKind, Variant};
|
||||
use rustc_hir::{FieldDef, Generics, HirId, Item, ItemKind, TraitRef, Ty, TyKind, Variant};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::middle::privacy::AccessLevels;
|
||||
use rustc_middle::middle::stability::{DeprecationEntry, Index};
|
||||
use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index};
|
||||
use rustc_middle::ty::{self, query::Providers, TyCtxt};
|
||||
use rustc_session::lint;
|
||||
use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED};
|
||||
|
@ -807,12 +807,46 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
|
|||
fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, id: hir::HirId) {
|
||||
if let Some(def_id) = path.res.opt_def_id() {
|
||||
let method_span = path.segments.last().map(|s| s.ident.span);
|
||||
self.tcx.check_stability(def_id, Some(id), path.span, method_span)
|
||||
self.tcx.check_stability_allow_unstable(
|
||||
def_id,
|
||||
Some(id),
|
||||
path.span,
|
||||
method_span,
|
||||
if is_unstable_reexport(self.tcx, id) {
|
||||
AllowUnstable::Yes
|
||||
} else {
|
||||
AllowUnstable::No
|
||||
},
|
||||
)
|
||||
}
|
||||
intravisit::walk_path(self, path)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether a path is a `use` item that has been marked as unstable.
|
||||
///
|
||||
/// See issue #94972 for details on why this is a special case
|
||||
fn is_unstable_reexport<'tcx>(tcx: TyCtxt<'tcx>, id: hir::HirId) -> bool {
|
||||
// Get the LocalDefId so we can lookup the item to check the kind.
|
||||
let Some(def_id) = tcx.hir().opt_local_def_id(id) else { return false; };
|
||||
|
||||
let Some(stab) = tcx.stability().local_stability(def_id) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if stab.level.is_stable() {
|
||||
// The re-export is not marked as unstable, don't override
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this is a path that isn't a use, we don't need to do anything special
|
||||
if !matches!(tcx.hir().item(hir::ItemId { def_id }).kind, ItemKind::Use(..)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
struct CheckTraitImplStable<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
fully_stable: bool,
|
||||
|
|
|
@ -24,6 +24,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
|
|||
use rustc_hir::intravisit::{walk_generics, Visitor as _};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{GenericArg, GenericArgs};
|
||||
use rustc_middle::middle::stability::AllowUnstable;
|
||||
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, Subst, SubstsRef};
|
||||
use rustc_middle::ty::GenericParamDefKind;
|
||||
use rustc_middle::ty::{self, Const, DefIdTree, EarlyBinder, Ty, TyCtxt, TypeFoldable};
|
||||
|
@ -426,6 +427,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
Some(arg.id()),
|
||||
arg.span(),
|
||||
None,
|
||||
AllowUnstable::No,
|
||||
|_, _| {
|
||||
// Default generic parameters may not be marked
|
||||
// with stability attributes, i.e. when the
|
||||
|
|
30
src/test/ui/stability-attribute/allow-unstable-reexport.rs
Normal file
30
src/test/ui/stability-attribute/allow-unstable-reexport.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Allow an unstable re-export without requiring a feature gate.
|
||||
// #94972
|
||||
|
||||
// aux-build:lint-stability.rs
|
||||
// aux-build:lint-stability-reexport.rs
|
||||
#![feature(staged_api)]
|
||||
#![stable(feature = "lint_stability", since = "1.0.0")]
|
||||
|
||||
extern crate lint_stability;
|
||||
extern crate lint_stability_reexport;
|
||||
|
||||
#[unstable(feature = "unstable_test_feature", issue = "none")]
|
||||
pub use lint_stability::unstable;
|
||||
|
||||
// We want to confirm that using a re-export through another crate behaves
|
||||
// the same way as using an item directly
|
||||
#[unstable(feature = "unstable_test_feature", issue = "none")]
|
||||
pub use lint_stability_reexport::unstable_text;
|
||||
|
||||
// Ensure items which aren't marked as unstable can't re-export unstable items
|
||||
#[stable(feature = "lint_stability", since = "1.0.0")]
|
||||
pub use lint_stability::unstable as unstable2;
|
||||
//~^ ERROR use of unstable library feature 'unstable_test_feature'
|
||||
|
||||
fn main() {
|
||||
// Since we didn't enable the feature in this crate, we still can't
|
||||
// use these items, even though they're in scope from the `use`s which are now allowed.
|
||||
unstable(); //~ ERROR use of unstable library feature 'unstable_test_feature'
|
||||
unstable_text(); //~ ERROR use of unstable library feature 'unstable_test_feature'
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
error[E0658]: use of unstable library feature 'unstable_test_feature'
|
||||
--> $DIR/allow-unstable-reexport.rs:22:9
|
||||
|
|
||||
LL | pub use lint_stability::unstable as unstable2;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: use of unstable library feature 'unstable_test_feature'
|
||||
--> $DIR/allow-unstable-reexport.rs:28:5
|
||||
|
|
||||
LL | unstable();
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: use of unstable library feature 'unstable_test_feature': text
|
||||
--> $DIR/allow-unstable-reexport.rs:29:5
|
||||
|
|
||||
LL | unstable_text();
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
|
@ -0,0 +1,9 @@
|
|||
#![crate_type = "lib"]
|
||||
#![feature(staged_api)]
|
||||
#![stable(feature = "lint_stability", since = "1.0.0")]
|
||||
|
||||
extern crate lint_stability;
|
||||
|
||||
// Re-exporting without enabling the feature "unstable_test_feature" in this crate
|
||||
#[unstable(feature = "unstable_test_feature", issue = "none")]
|
||||
pub use lint_stability::unstable_text;
|
Loading…
Add table
Reference in a new issue