Skip to content

Commit fe94c91

Browse files
committed
allowed_through_unstable_modules: support showing a deprecation message when the unstable module name is used
1 parent f6d6874 commit fe94c91

File tree

10 files changed

+125
-51
lines changed

10 files changed

+125
-51
lines changed

Diff for: compiler/rustc_ast/src/attr/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,8 @@ impl MetaItemLit {
723723
pub trait AttributeExt: Debug {
724724
fn id(&self) -> AttrId;
725725

726+
/// For a single-segment attribute (i.e., `#[attr]` and not `#[path::atrr]`),
727+
/// return the name of the attribute, else return the empty identifier.
726728
fn name_or_empty(&self) -> Symbol {
727729
self.ident().unwrap_or_else(Ident::empty).name
728730
}

Diff for: compiler/rustc_attr_data_structures/src/stability.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,10 @@ pub enum StabilityLevel {
132132
Stable {
133133
/// Rust release which stabilized this feature.
134134
since: StableSince,
135-
/// Is this item allowed to be referred to on stable, despite being contained in unstable
136-
/// modules?
137-
allowed_through_unstable_modules: bool,
135+
/// This is `Some` if this item allowed to be referred to on stable, despite being contained
136+
/// in unstable modules. `Some(Some(sym))` indicates that that usage is deprecated, with the
137+
/// given deprecation message.
138+
allowed_through_unstable_modules: Option<Option<Symbol>>,
138139
},
139140
}
140141

Diff for: compiler/rustc_attr_parsing/src/attributes/stability.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ pub fn find_stability(
2424
item_sp: Span,
2525
) -> Option<(Stability, Span)> {
2626
let mut stab: Option<(Stability, Span)> = None;
27-
let mut allowed_through_unstable_modules = false;
27+
let mut allowed_through_unstable_modules = None;
2828

2929
for attr in attrs {
3030
match attr.name_or_empty() {
31-
sym::rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true,
31+
sym::rustc_allowed_through_unstable_modules => {
32+
allowed_through_unstable_modules = Some(attr.value_str())
33+
}
3234
sym::unstable => {
3335
if stab.is_some() {
3436
sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
@@ -56,15 +58,15 @@ pub fn find_stability(
5658
}
5759
}
5860

59-
if allowed_through_unstable_modules {
61+
if let Some(allowed_through_unstable_modules) = allowed_through_unstable_modules {
6062
match &mut stab {
6163
Some((
6264
Stability {
63-
level: StabilityLevel::Stable { allowed_through_unstable_modules, .. },
65+
level: StabilityLevel::Stable { allowed_through_unstable_modules: in_stab, .. },
6466
..
6567
},
6668
_,
67-
)) => *allowed_through_unstable_modules = true,
69+
)) => *in_stab = Some(allowed_through_unstable_modules),
6870
_ => {
6971
sess.dcx()
7072
.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
@@ -283,7 +285,7 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
283285

284286
match feature {
285287
Ok(feature) => {
286-
let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false };
288+
let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: None };
287289
Some((feature, level))
288290
}
289291
Err(ErrorGuaranteed { .. }) => None,

Diff for: compiler/rustc_feature/src/builtin_attrs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
622622
EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint",
623623
),
624624
rustc_attr!(
625-
rustc_allowed_through_unstable_modules, Normal, template!(Word),
625+
rustc_allowed_through_unstable_modules, Normal, template!(Word, NameValueStr: "deprecation message"),
626626
WarnFollowing, EncodeCrossCrate::No,
627627
"rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
628628
through unstable paths"

Diff for: compiler/rustc_passes/src/stability.rs

+93-37
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,16 @@ use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant};
2020
use rustc_middle::hir::nested_filter;
2121
use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures};
2222
use rustc_middle::middle::privacy::EffectiveVisibilities;
23-
use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index};
23+
use rustc_middle::middle::stability::{
24+
AllowUnstable, Deprecated, DeprecationEntry, EvalResult, Index,
25+
};
2426
use rustc_middle::query::Providers;
2527
use rustc_middle::ty::TyCtxt;
28+
use rustc_middle::ty::print::with_no_trimmed_paths;
2629
use rustc_session::lint;
27-
use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED};
30+
use rustc_session::lint::builtin::{
31+
DEPRECATED, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED,
32+
};
2833
use rustc_span::{Span, Symbol, sym};
2934
use tracing::{debug, info};
3035

@@ -844,42 +849,93 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
844849
},
845850
);
846851

847-
let is_allowed_through_unstable_modules = |def_id| {
848-
self.tcx.lookup_stability(def_id).is_some_and(|stab| match stab.level {
849-
StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
850-
allowed_through_unstable_modules
852+
if item_is_allowed {
853+
// The item itself is allowed; check whether the path there is also allowed.
854+
let is_allowed_through_unstable_modules: Option<Option<Symbol>> =
855+
self.tcx.lookup_stability(def_id).and_then(|stab| match stab.level {
856+
StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
857+
allowed_through_unstable_modules
858+
}
859+
_ => None,
860+
});
861+
862+
if is_allowed_through_unstable_modules.is_none() {
863+
// Check parent modules stability as well if the item the path refers to is itself
864+
// stable. We only emit warnings for unstable path segments if the item is stable
865+
// or allowed because stability is often inherited, so the most common case is that
866+
// both the segments and the item are unstable behind the same feature flag.
867+
//
868+
// We check here rather than in `visit_path_segment` to prevent visiting the last
869+
// path segment twice
870+
//
871+
// We include special cases via #[rustc_allowed_through_unstable_modules] for items
872+
// that were accidentally stabilized through unstable paths before this check was
873+
// added, such as `core::intrinsics::transmute`
874+
let parents = path.segments.iter().rev().skip(1);
875+
for path_segment in parents {
876+
if let Some(def_id) = path_segment.res.opt_def_id() {
877+
// use `None` for id to prevent deprecation check
878+
self.tcx.check_stability_allow_unstable(
879+
def_id,
880+
None,
881+
path.span,
882+
None,
883+
if is_unstable_reexport(self.tcx, id) {
884+
AllowUnstable::Yes
885+
} else {
886+
AllowUnstable::No
887+
},
888+
);
889+
}
851890
}
852-
_ => false,
853-
})
854-
};
855-
856-
if item_is_allowed && !is_allowed_through_unstable_modules(def_id) {
857-
// Check parent modules stability as well if the item the path refers to is itself
858-
// stable. We only emit warnings for unstable path segments if the item is stable
859-
// or allowed because stability is often inherited, so the most common case is that
860-
// both the segments and the item are unstable behind the same feature flag.
861-
//
862-
// We check here rather than in `visit_path_segment` to prevent visiting the last
863-
// path segment twice
864-
//
865-
// We include special cases via #[rustc_allowed_through_unstable_modules] for items
866-
// that were accidentally stabilized through unstable paths before this check was
867-
// added, such as `core::intrinsics::transmute`
868-
let parents = path.segments.iter().rev().skip(1);
869-
for path_segment in parents {
870-
if let Some(def_id) = path_segment.res.opt_def_id() {
871-
// use `None` for id to prevent deprecation check
872-
self.tcx.check_stability_allow_unstable(
873-
def_id,
874-
None,
875-
path.span,
876-
None,
877-
if is_unstable_reexport(self.tcx, id) {
878-
AllowUnstable::Yes
879-
} else {
880-
AllowUnstable::No
881-
},
882-
);
891+
} else if let Some(Some(deprecation)) = is_allowed_through_unstable_modules {
892+
// Similar to above, but we cannot use `check_stability_allow_unstable` as that would
893+
// immediately show the stability error. We just want to know the result and disaplay
894+
// our own kind of error.
895+
let parents = path.segments.iter().rev().skip(1);
896+
for path_segment in parents {
897+
if let Some(def_id) = path_segment.res.opt_def_id() {
898+
// use `None` for id to prevent deprecation check
899+
let eval_result = self.tcx.eval_stability_allow_unstable(
900+
def_id,
901+
None,
902+
path.span,
903+
None,
904+
if is_unstable_reexport(self.tcx, id) {
905+
AllowUnstable::Yes
906+
} else {
907+
AllowUnstable::No
908+
},
909+
);
910+
let is_allowed = matches!(eval_result, EvalResult::Allow);
911+
if !is_allowed {
912+
// Calculating message for lint involves calling `self.def_path_str`,
913+
// which will by default invoke the expensive `visible_parent_map` query.
914+
// Skip all that work if the lint is allowed anyway.
915+
if self.tcx.lint_level_at_node(DEPRECATED, id).0
916+
== lint::Level::Allow
917+
{
918+
return;
919+
}
920+
// Show a deprecation message.
921+
let def_path =
922+
with_no_trimmed_paths!(self.tcx.def_path_str(def_id));
923+
let def_kind = self.tcx.def_descr(def_id);
924+
let diag = Deprecated {
925+
sub: None,
926+
kind: def_kind.to_owned(),
927+
path: def_path,
928+
note: Some(deprecation),
929+
since_kind: lint::DeprecatedSinceKind::InEffect,
930+
};
931+
self.tcx.emit_node_span_lint(
932+
DEPRECATED,
933+
id,
934+
method_span.unwrap_or(path.span),
935+
diag,
936+
);
937+
}
938+
}
883939
}
884940
}
885941
}

Diff for: src/librustdoc/passes/propagate_stability.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ fn merge_stability(
100100
parent_stability: Option<Stability>,
101101
) -> Option<Stability> {
102102
if let Some(own_stab) = own_stability
103-
&& let StabilityLevel::Stable { since: own_since, allowed_through_unstable_modules: false } =
103+
&& let StabilityLevel::Stable { since: own_since, allowed_through_unstable_modules: None } =
104104
own_stab.level
105105
&& let Some(parent_stab) = parent_stability
106106
&& (parent_stab.is_unstable()

Diff for: src/tools/clippy/clippy_lints/src/std_instead_of_core.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: &Msrv) -> bool {
180180
if let Some(stability) = cx.tcx.lookup_stability(def_id)
181181
&& let StabilityLevel::Stable {
182182
since,
183-
allowed_through_unstable_modules: false,
183+
allowed_through_unstable_modules: None,
184184
} = stability.level
185185
{
186186
let stable = match since {

Diff for: tests/ui/stability-attribute/allowed-through-unstable.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@
66
extern crate allowed_through_unstable_core;
77

88
use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstable;
9+
use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstableWithDeprecation; //~WARN use of deprecated module `allowed_through_unstable_core::unstable_module`: use the new path instead
910
use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowedThroughUnstable; //~ ERROR use of unstable library feature `unstable_test_feature`
+10-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1+
warning: use of deprecated module `allowed_through_unstable_core::unstable_module`: use the new path instead
2+
--> $DIR/allowed-through-unstable.rs:9:53
3+
|
4+
LL | use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstableWithDeprecation;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(deprecated)]` on by default
8+
19
error[E0658]: use of unstable library feature `unstable_test_feature`
2-
--> $DIR/allowed-through-unstable.rs:9:5
10+
--> $DIR/allowed-through-unstable.rs:10:5
311
|
412
LL | use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowedThroughUnstable;
513
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,6 +16,6 @@ LL | use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowe
816
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
917
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
1018

11-
error: aborting due to 1 previous error
19+
error: aborting due to 1 previous error; 1 warning emitted
1220

1321
For more information about this error, try `rustc --explain E0658`.

Diff for: tests/ui/stability-attribute/auxiliary/allowed-through-unstable-core.rs

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ pub mod unstable_module {
99
#[rustc_allowed_through_unstable_modules]
1010
pub trait OldStableTraitAllowedThoughUnstable {}
1111

12+
#[stable(feature = "stable_test_feature", since = "1.2.0")]
13+
#[rustc_allowed_through_unstable_modules = "use the new path instead"]
14+
pub trait OldStableTraitAllowedThoughUnstableWithDeprecation {}
15+
1216
#[stable(feature = "stable_test_feature", since = "1.2.0")]
1317
pub trait NewStableTraitNotAllowedThroughUnstable {}
1418
}

0 commit comments

Comments
 (0)