Skip to content

Commit eb8d2f9

Browse files
committed
Use TrackedFeatures to track used features when calling enabled
1 parent 66daca1 commit eb8d2f9

7 files changed

Lines changed: 127 additions & 17 deletions

File tree

compiler/rustc_feature/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,5 +136,6 @@ pub use builtin_attrs::{
136136
};
137137
pub use removed::REMOVED_LANG_FEATURES;
138138
pub use unstable::{
139-
EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES, UNSTABLE_LANG_FEATURES,
139+
EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES, TrackedFeatures,
140+
UNSTABLE_LANG_FEATURES,
140141
};

compiler/rustc_feature/src/unstable.rs

Lines changed: 86 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! List of the unstable feature gates.
22
33
use std::path::PathBuf;
4+
use std::sync::{LazyLock, Mutex};
45
use std::time::{SystemTime, UNIX_EPOCH};
56

67
use rustc_data_structures::fx::FxHashSet;
@@ -27,6 +28,9 @@ macro_rules! status_to_enum {
2728
};
2829
}
2930

31+
static USED_FEATURES: LazyLock<Mutex<FxHashSet<Symbol>>> =
32+
LazyLock::new(|| Mutex::new(FxHashSet::default()));
33+
3034
/// A set of features to be used by later passes.
3135
///
3236
/// There are two ways to check if a language feature `foo` is enabled:
@@ -35,6 +39,79 @@ macro_rules! status_to_enum {
3539
///
3640
/// The former is preferred. `enabled` should only be used when the feature symbol is not a
3741
/// constant, e.g. a parameter, or when the feature is a library feature.
42+
///
43+
/// NOTE: Do NOT use any other method (e.g. `enabled_features`) to check if a feature is enabled.
44+
/// Only `enabled(&self, feature: Symbol)` and the generated `foo(&self)` methods will record
45+
/// feature usage. Using any other method will silently skip tracking and lead to incorrect
46+
/// "unused features" diagnostics.
47+
#[derive(Clone, Debug)]
48+
pub struct TrackedFeatures {
49+
features: Features,
50+
}
51+
52+
impl TrackedFeatures {
53+
pub fn new(features: Features) -> Self {
54+
Self { features }
55+
}
56+
57+
pub fn unused_features(&self) -> Vec<(Symbol, Span)> {
58+
let used_features = USED_FEATURES.lock().unwrap();
59+
self.enabled_features_iter_stable_order()
60+
.filter(|(f, _)| !used_features.contains(f))
61+
.collect()
62+
}
63+
64+
/// Is the given feature enabled (via `#[feature(...)]`)?
65+
pub fn enabled(&self, feature: Symbol) -> bool {
66+
USED_FEATURES.lock().unwrap().insert(feature);
67+
self.features.enabled(feature)
68+
}
69+
70+
/// Returns a list of [`EnabledLangFeature`] with info about:
71+
///
72+
/// - Feature gate name.
73+
/// - The span of the `#[feature]` attribute.
74+
/// - For stable language features, version info for when it was stabilized.
75+
pub fn enabled_lang_features(&self) -> &Vec<EnabledLangFeature> {
76+
self.features.enabled_lang_features()
77+
}
78+
79+
pub fn enabled_lib_features(&self) -> &Vec<EnabledLibFeature> {
80+
self.features.enabled_lib_features()
81+
}
82+
83+
pub fn enabled_features(&self) -> &FxHashSet<Symbol> {
84+
&self.features.enabled_features()
85+
}
86+
87+
/// Returns a iterator of enabled features in stable order.
88+
pub fn enabled_features_iter_stable_order(
89+
&self,
90+
) -> impl Iterator<Item = (Symbol, Span)> + Clone {
91+
self.features.enabled_features_iter_stable_order()
92+
}
93+
94+
pub fn dump_feature_usage_metrics(
95+
&self,
96+
metrics_path: PathBuf,
97+
) -> Result<(), Box<dyn std::error::Error>> {
98+
self.features.dump_feature_usage_metrics(metrics_path)
99+
}
100+
101+
/// Some features are known to be incomplete and using them is likely to have
102+
/// unanticipated results, such as compiler crashes. We warn the user about these
103+
/// to alert them.
104+
pub fn incomplete(&self, feature: Symbol) -> bool {
105+
self.features.incomplete(feature)
106+
}
107+
108+
/// Some features are internal to the compiler and standard library and should not
109+
/// be used in normal projects. We warn the user about these to alert them.
110+
pub fn internal(&self, feature: Symbol) -> bool {
111+
self.features.internal(feature)
112+
}
113+
}
114+
38115
#[derive(Clone, Default, Debug)]
39116
pub struct Features {
40117
/// `#![feature]` attrs for language features, for error reporting.
@@ -89,22 +166,18 @@ impl Features {
89166
&self.enabled_lib_features
90167
}
91168

92-
pub fn enabled_features(&self) -> &FxHashSet<Symbol> {
169+
fn enabled_features(&self) -> &FxHashSet<Symbol> {
93170
&self.enabled_features
94171
}
95172

96-
/// Returns a iterator of enabled features in stable order.
97-
pub fn enabled_features_iter_stable_order(
98-
&self,
99-
) -> impl Iterator<Item = (Symbol, Span)> + Clone {
173+
fn enabled_features_iter_stable_order(&self) -> impl Iterator<Item = (Symbol, Span)> + Clone {
100174
self.enabled_lang_features
101175
.iter()
102176
.map(|feat| (feat.gate_name, feat.attr_sp))
103177
.chain(self.enabled_lib_features.iter().map(|feat| (feat.gate_name, feat.attr_sp)))
104178
}
105179

106-
/// Is the given feature enabled (via `#[feature(...)]`)?
107-
pub fn enabled(&self, feature: Symbol) -> bool {
180+
fn enabled(&self, feature: Symbol) -> bool {
108181
self.enabled_features.contains(&feature)
109182
}
110183
}
@@ -123,17 +196,19 @@ macro_rules! declare_features {
123196
}),+
124197
];
125198

126-
impl Features {
199+
impl TrackedFeatures {
127200
$(
128201
pub fn $feature(&self) -> bool {
129-
self.enabled_features.contains(&sym::$feature)
202+
self.enabled(sym::$feature)
130203
}
131204
)*
205+
}
132206

207+
impl Features {
133208
/// Some features are known to be incomplete and using them is likely to have
134209
/// unanticipated results, such as compiler crashes. We warn the user about these
135210
/// to alert them.
136-
pub fn incomplete(&self, feature: Symbol) -> bool {
211+
fn incomplete(&self, feature: Symbol) -> bool {
137212
match feature {
138213
$(
139214
sym::$feature => status_to_enum!($status) == FeatureStatus::Incomplete,
@@ -711,7 +786,7 @@ declare_features! (
711786
);
712787

713788
impl Features {
714-
pub fn dump_feature_usage_metrics(
789+
fn dump_feature_usage_metrics(
715790
&self,
716791
metrics_path: PathBuf,
717792
) -> Result<(), Box<dyn std::error::Error>> {

compiler/rustc_interface/src/passes.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,9 +875,15 @@ pub fn write_interface<'tcx>(tcx: TyCtxt<'tcx>) {
875875
}
876876
}
877877

878+
pub fn tracked_features_query<'tcx>(tcx: TyCtxt<'tcx>) -> &'tcx rustc_feature::TrackedFeatures {
879+
let features = tcx.features_query(());
880+
tcx.arena.alloc(rustc_feature::TrackedFeatures::new(features.clone()))
881+
}
882+
878883
pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| {
879884
let providers = &mut Providers::default();
880885
providers.queries.analysis = analysis;
886+
providers.queries.tracked_features_query = |tcx, _| tracked_features_query(tcx);
881887
providers.queries.hir_crate = rustc_ast_lowering::lower_to_hir;
882888
providers.queries.resolver_for_lowering_raw = resolver_for_lowering_raw;
883889
providers.queries.stripped_cfg_items = |tcx, _| &tcx.resolutions(()).stripped_cfg_items[..];

compiler/rustc_middle/src/arena.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ macro_rules! arena_types {
116116
[] stripped_cfg_items: rustc_hir::attrs::StrippedCfgItem,
117117
[] mod_child: rustc_middle::metadata::ModChild,
118118
[] features: rustc_feature::Features,
119+
[] tracked_features: rustc_feature::TrackedFeatures,
119120
[decode] specialization_graph: rustc_middle::traits::specialization_graph::Graph,
120121
[] crate_inherent_impls: rustc_middle::ty::CrateInherentImpls,
121122
[] hir_owner_nodes: rustc_hir::OwnerNodes<'tcx>,

compiler/rustc_middle/src/queries.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2610,6 +2610,11 @@ rustc_queries! {
26102610

26112611
query features_query(_: ()) -> &'tcx rustc_feature::Features {
26122612
feedable
2613+
desc { "looking up raw feature gates" }
2614+
}
2615+
2616+
query tracked_features_query(_: ()) -> &'tcx rustc_feature::TrackedFeatures {
2617+
eval_always
26132618
desc { "looking up enabled feature gates" }
26142619
}
26152620

compiler/rustc_middle/src/ty/context.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use rustc_hir::definitions::{DefPathData, Definitions, DisambiguatorState};
3838
use rustc_hir::intravisit::VisitorExt;
3939
use rustc_hir::lang_items::LangItem;
4040
use rustc_hir::limit::Limit;
41-
use rustc_hir::{self as hir, HirId, Node, TraitCandidate, find_attr};
41+
use rustc_hir::{self as hir, CRATE_HIR_ID, HirId, Node, TraitCandidate, find_attr};
4242
use rustc_index::IndexVec;
4343
use rustc_query_system::cache::WithDepNode;
4444
use rustc_query_system::dep_graph::DepNodeIndex;
@@ -356,7 +356,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
356356
self.recursion_limit().0
357357
}
358358

359-
type Features = &'tcx rustc_feature::Features;
359+
type Features = &'tcx rustc_feature::TrackedFeatures;
360360

361361
fn features(self) -> Self::Features {
362362
self.features()
@@ -893,7 +893,9 @@ impl<'tcx> rustc_type_ir::inherent::Safety<TyCtxt<'tcx>> for hir::Safety {
893893
}
894894
}
895895

896-
impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_feature::Features {
896+
impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>>
897+
for &'tcx rustc_feature::TrackedFeatures
898+
{
897899
fn generic_const_exprs(self) -> bool {
898900
self.generic_const_exprs()
899901
}
@@ -1934,8 +1936,8 @@ impl<'tcx> TyCtxt<'tcx> {
19341936
)
19351937
}
19361938

1937-
pub fn features(self) -> &'tcx rustc_feature::Features {
1938-
self.features_query(())
1939+
pub fn features(self) -> &'tcx rustc_feature::TrackedFeatures {
1940+
self.tracked_features_query(())
19391941
}
19401942

19411943
pub fn def_key(self, id: impl IntoQueryParam<DefId>) -> rustc_hir::definitions::DefKey {
@@ -2446,6 +2448,17 @@ impl<'tcx> TyCtxt<'tcx> {
24462448
}
24472449

24482450
pub fn finish(self) {
2451+
for (feature, span) in self.features().unused_features() {
2452+
self.node_span_lint(
2453+
rustc_session::lint::builtin::UNUSED_FEATURES,
2454+
CRATE_HIR_ID,
2455+
span,
2456+
|lint| {
2457+
lint.primary_message(format!("feature `{}` is declared but not used", feature));
2458+
},
2459+
);
2460+
}
2461+
24492462
// We assume that no queries are run past here. If there are new queries
24502463
// after this point, they'll show up as "<unknown>" in self-profiling data.
24512464
self.alloc_self_profile_query_strings();

compiler/rustc_query_system/src/ich/impls_syntax.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ impl<'tcx> HashStable<StableHashingContext<'tcx>> for rustc_feature::Features {
100100
}
101101
}
102102

103+
impl<'tcx> HashStable<StableHashingContext<'tcx>> for rustc_feature::TrackedFeatures {
104+
fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) {
105+
// Unfortunately we cannot exhaustively list fields here, since the
106+
// struct has private fields (to ensure its invariant is maintained)
107+
self.enabled_lang_features().hash_stable(hcx, hasher);
108+
self.enabled_lib_features().hash_stable(hcx, hasher);
109+
}
110+
}
111+
103112
impl<'tcx> HashStable<StableHashingContext<'tcx>> for rustc_feature::EnabledLangFeature {
104113
fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) {
105114
let rustc_feature::EnabledLangFeature { gate_name, attr_sp, stable_since } = self;

0 commit comments

Comments
 (0)