@@ -13,17 +13,16 @@ use rustc_ast::NodeId;
13
13
use rustc_ast:: { self as ast, AttrStyle , Attribute , HasAttrs , HasTokens , MetaItem } ;
14
14
use rustc_attr as attr;
15
15
use rustc_data_structures:: flat_map_in_place:: FlatMapInPlace ;
16
- use rustc_data_structures:: fx:: FxHashMap ;
16
+ use rustc_data_structures:: fx:: FxHashSet ;
17
17
use rustc_feature:: { Feature , Features , State as FeatureState } ;
18
- use rustc_feature:: {
19
- ACCEPTED_FEATURES , ACTIVE_FEATURES , REMOVED_FEATURES , STABLE_REMOVED_FEATURES ,
20
- } ;
18
+ use rustc_feature:: { ACCEPTED_FEATURES , ACTIVE_FEATURES , REMOVED_FEATURES } ;
21
19
use rustc_parse:: validate_attr;
22
20
use rustc_session:: parse:: feature_err;
23
21
use rustc_session:: Session ;
24
22
use rustc_span:: edition:: { Edition , ALL_EDITIONS } ;
25
23
use rustc_span:: symbol:: { sym, Symbol } ;
26
- use rustc_span:: { Span , DUMMY_SP } ;
24
+ use rustc_span:: Span ;
25
+ use thin_vec:: ThinVec ;
27
26
28
27
/// A folder that strips out items that do not belong in the current configuration.
29
28
pub struct StripUnconfigured < ' a > {
@@ -37,13 +36,6 @@ pub struct StripUnconfigured<'a> {
37
36
}
38
37
39
38
pub fn features ( sess : & Session , krate_attrs : & [ Attribute ] ) -> Features {
40
- fn feature_removed ( sess : & Session , span : Span , reason : Option < & str > ) {
41
- sess. emit_err ( FeatureRemoved {
42
- span,
43
- reason : reason. map ( |reason| FeatureRemovedReason { reason } ) ,
44
- } ) ;
45
- }
46
-
47
39
fn active_features_up_to ( edition : Edition ) -> impl Iterator < Item = & ' static Feature > {
48
40
ACTIVE_FEATURES . iter ( ) . filter ( move |feature| {
49
41
if let Some ( feature_edition) = feature. edition {
@@ -54,67 +46,49 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
54
46
} )
55
47
}
56
48
57
- let mut features = Features :: default ( ) ;
58
- let mut edition_enabled_features = FxHashMap :: default ( ) ;
59
- let crate_edition = sess. edition ( ) ;
60
-
61
- for & edition in ALL_EDITIONS {
62
- if edition <= crate_edition {
63
- // The `crate_edition` implies its respective umbrella feature-gate
64
- // (i.e., `#![feature(rust_20XX_preview)]` isn't needed on edition 20XX).
65
- edition_enabled_features. insert ( edition. feature_name ( ) , edition) ;
49
+ fn feature_list ( attr : & Attribute ) -> ThinVec < ast:: NestedMetaItem > {
50
+ if attr. has_name ( sym:: feature) && let Some ( list) = attr. meta_item_list ( ) {
51
+ list
52
+ } else {
53
+ ThinVec :: new ( )
66
54
}
67
55
}
68
56
69
- for feature in active_features_up_to ( crate_edition) {
70
- feature. set ( & mut features, DUMMY_SP ) ;
71
- edition_enabled_features. insert ( feature. name , crate_edition) ;
72
- }
73
-
74
- // Process the edition umbrella feature-gates first, to ensure
75
- // `edition_enabled_features` is completed before it's queried.
76
- for attr in krate_attrs {
77
- if !attr. has_name ( sym:: feature) {
78
- continue ;
79
- }
80
-
81
- let Some ( list) = attr. meta_item_list ( ) else {
82
- continue ;
83
- } ;
84
-
85
- for mi in list {
86
- if !mi. is_word ( ) {
87
- continue ;
88
- }
89
-
90
- let name = mi. name_or_empty ( ) ;
57
+ let mut features = Features :: default ( ) ;
91
58
92
- let edition = ALL_EDITIONS . iter ( ) . find ( |e| name == e. feature_name ( ) ) . copied ( ) ;
93
- if let Some ( edition) = edition {
94
- if edition <= crate_edition {
95
- continue ;
96
- }
59
+ // The edition from `--edition`.
60
+ let crate_edition = sess. edition ( ) ;
97
61
98
- for feature in active_features_up_to ( edition) {
99
- // FIXME(Manishearth) there is currently no way to set
100
- // lib features by edition
101
- feature. set ( & mut features, DUMMY_SP ) ;
102
- edition_enabled_features. insert ( feature. name , edition) ;
62
+ // The maximum of (a) the edition from `--edition` and (b) any edition
63
+ // umbrella feature-gates declared in the code.
64
+ // - E.g. if `crate_edition` is 2015 but `rust_2018_preview` is present,
65
+ // `feature_edition` is 2018
66
+ let mut features_edition = crate_edition;
67
+ for attr in krate_attrs {
68
+ for mi in feature_list ( attr) {
69
+ if mi. is_word ( ) {
70
+ let name = mi. name_or_empty ( ) ;
71
+ let edition = ALL_EDITIONS . iter ( ) . find ( |e| name == e. feature_name ( ) ) . copied ( ) ;
72
+ if let Some ( edition) = edition && edition > features_edition {
73
+ features_edition = edition;
103
74
}
104
75
}
105
76
}
106
77
}
107
78
108
- for attr in krate_attrs {
109
- if !attr. has_name ( sym:: feature) {
110
- continue ;
111
- }
112
-
113
- let Some ( list) = attr. meta_item_list ( ) else {
114
- continue ;
115
- } ;
79
+ // Enable edition-dependent features based on `features_edition`.
80
+ // - E.g. enable `test_2018_feature` if `features_edition` is 2018 or higher
81
+ let mut edition_enabled_features = FxHashSet :: default ( ) ;
82
+ for feature in active_features_up_to ( features_edition) {
83
+ // FIXME(Manishearth) there is currently no way to set lib features by
84
+ // edition.
85
+ edition_enabled_features. insert ( feature. name ) ;
86
+ feature. set ( & mut features) ;
87
+ }
116
88
117
- for mi in list {
89
+ // Process all features declared in the code.
90
+ for attr in krate_attrs {
91
+ for mi in feature_list ( attr) {
118
92
let name = match mi. ident ( ) {
119
93
Some ( ident) if mi. is_word ( ) => ident. name ,
120
94
Some ( ident) => {
@@ -136,54 +110,76 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
136
110
}
137
111
} ;
138
112
139
- if let Some ( & edition) = edition_enabled_features. get ( & name) {
113
+ // If the declared feature is an edition umbrella feature-gate,
114
+ // warn if it was redundant w.r.t. `crate_edition`.
115
+ // - E.g. warn if `rust_2018_preview` is declared when
116
+ // `crate_edition` is 2018
117
+ // - E.g. don't warn if `rust_2018_preview` is declared when
118
+ // `crate_edition` is 2015.
119
+ if let Some ( & edition) = ALL_EDITIONS . iter ( ) . find ( |e| name == e. feature_name ( ) ) {
120
+ if edition <= crate_edition {
121
+ sess. emit_warning ( FeatureIncludedInEdition {
122
+ span : mi. span ( ) ,
123
+ feature : name,
124
+ edition,
125
+ } ) ;
126
+ }
127
+ features. set_declared_lang_feature ( name, mi. span ( ) , None ) ;
128
+ continue ;
129
+ }
130
+
131
+ // If the declared feature is edition-dependent and was already
132
+ // enabled due to `feature_edition`, give a warning.
133
+ // - E.g. warn if `test_2018_feature` is declared when
134
+ // `feature_edition` is 2018 or higher.
135
+ if edition_enabled_features. contains ( & name) {
140
136
sess. emit_warning ( FeatureIncludedInEdition {
141
137
span : mi. span ( ) ,
142
138
feature : name,
143
- edition,
139
+ edition : features_edition ,
144
140
} ) ;
141
+ features. set_declared_lang_feature ( name, mi. span ( ) , None ) ;
145
142
continue ;
146
143
}
147
144
148
- if ALL_EDITIONS . iter ( ) . any ( |e| name == e. feature_name ( ) ) {
149
- // Handled in the separate loop above.
150
- continue ;
151
- }
152
-
153
- let removed = REMOVED_FEATURES . iter ( ) . find ( |f| name == f. name ) ;
154
- let stable_removed = STABLE_REMOVED_FEATURES . iter ( ) . find ( |f| name == f. name ) ;
155
- if let Some ( Feature { state, .. } ) = removed. or ( stable_removed) {
156
- if let FeatureState :: Removed { reason } | FeatureState :: Stabilized { reason } =
157
- state
158
- {
159
- feature_removed ( sess, mi. span ( ) , * reason) ;
145
+ // If the declared feature has been removed, issue an error.
146
+ if let Some ( Feature { state, .. } ) = REMOVED_FEATURES . iter ( ) . find ( |f| name == f. name ) {
147
+ if let FeatureState :: Removed { reason } = state {
148
+ sess. emit_err ( FeatureRemoved {
149
+ span : mi. span ( ) ,
150
+ reason : reason. map ( |reason| FeatureRemovedReason { reason } ) ,
151
+ } ) ;
160
152
continue ;
161
153
}
162
154
}
163
155
156
+ // If the declared feature is stable, record it.
164
157
if let Some ( Feature { since, .. } ) = ACCEPTED_FEATURES . iter ( ) . find ( |f| name == f. name ) {
165
158
let since = Some ( Symbol :: intern ( since) ) ;
166
- features. declared_lang_features . push ( ( name, mi. span ( ) , since) ) ;
167
- features. active_features . insert ( name) ;
159
+ features. set_declared_lang_feature ( name, mi. span ( ) , since) ;
168
160
continue ;
169
161
}
170
162
163
+ // If `-Z allow-features` is used and the declared feature is
164
+ // unstable and not also listed as one of the allowed features,
165
+ // issue an error.
171
166
if let Some ( allowed) = sess. opts . unstable_opts . allow_features . as_ref ( ) {
172
167
if allowed. iter ( ) . all ( |f| name. as_str ( ) != f) {
173
168
sess. emit_err ( FeatureNotAllowed { span : mi. span ( ) , name } ) ;
174
169
continue ;
175
170
}
176
171
}
177
172
173
+ // If the declared feature is unstable, record it.
178
174
if let Some ( f) = ACTIVE_FEATURES . iter ( ) . find ( |f| name == f. name ) {
179
- f. set ( & mut features, mi. span ( ) ) ;
180
- features. declared_lang_features . push ( ( name, mi. span ( ) , None ) ) ;
181
- features. active_features . insert ( name) ;
175
+ f. set ( & mut features) ;
176
+ features. set_declared_lang_feature ( name, mi. span ( ) , None ) ;
182
177
continue ;
183
178
}
184
179
185
- features. declared_lib_features . push ( ( name, mi. span ( ) ) ) ;
186
- features. active_features . insert ( name) ;
180
+ // Otherwise, the feature is unknown. Record it as a lib feature.
181
+ // It will be checked later.
182
+ features. set_declared_lib_feature ( name, mi. span ( ) ) ;
187
183
}
188
184
}
189
185
0 commit comments