]> git.lizzy.rs Git - rust.git/blob - src/librustc_parse/config.rs
Rollup merge of #68033 - ollie27:win_f32, r=dtolnay
[rust.git] / src / librustc_parse / config.rs
1 //! Process the potential `cfg` attributes on a module.
2 //! Also determine if the module should be included in this configuration.
3 //!
4 //! This module properly belongs in rustc_expand, but for now it's tied into
5 //! parsing, so we leave it here to avoid complicated out-of-line dependencies.
6 //!
7 //! A principled solution to this wrong location would be to implement [#64197].
8 //!
9 //! [#64197]: https://github.com/rust-lang/rust/issues/64197
10
11 use crate::{parse_in, validate_attr};
12 use rustc_data_structures::fx::FxHashMap;
13 use rustc_error_codes::*;
14 use rustc_errors::{error_code, struct_span_err, Applicability, Handler};
15 use rustc_feature::{Feature, Features, State as FeatureState};
16 use rustc_feature::{
17     ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES,
18 };
19 use rustc_span::edition::{Edition, ALL_EDITIONS};
20 use rustc_span::symbol::{sym, Symbol};
21 use rustc_span::{Span, DUMMY_SP};
22 use syntax::ast::{self, AttrItem, Attribute, MetaItem};
23 use syntax::attr;
24 use syntax::attr::HasAttrs;
25 use syntax::mut_visit::*;
26 use syntax::ptr::P;
27 use syntax::sess::{feature_err, ParseSess};
28 use syntax::util::map_in_place::MapInPlace;
29
30 use smallvec::SmallVec;
31
32 /// A folder that strips out items that do not belong in the current configuration.
33 pub struct StripUnconfigured<'a> {
34     pub sess: &'a ParseSess,
35     pub features: Option<&'a Features>,
36 }
37
38 fn get_features(
39     span_handler: &Handler,
40     krate_attrs: &[ast::Attribute],
41     crate_edition: Edition,
42     allow_features: &Option<Vec<String>>,
43 ) -> Features {
44     fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) {
45         let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed");
46         err.span_label(span, "feature has been removed");
47         if let Some(reason) = reason {
48             err.note(reason);
49         }
50         err.emit();
51     }
52
53     fn active_features_up_to(edition: Edition) -> impl Iterator<Item = &'static Feature> {
54         ACTIVE_FEATURES.iter().filter(move |feature| {
55             if let Some(feature_edition) = feature.edition {
56                 feature_edition <= edition
57             } else {
58                 false
59             }
60         })
61     }
62
63     let mut features = Features::default();
64     let mut edition_enabled_features = FxHashMap::default();
65
66     for &edition in ALL_EDITIONS {
67         if edition <= crate_edition {
68             // The `crate_edition` implies its respective umbrella feature-gate
69             // (i.e., `#![feature(rust_20XX_preview)]` isn't needed on edition 20XX).
70             edition_enabled_features.insert(edition.feature_name(), edition);
71         }
72     }
73
74     for feature in active_features_up_to(crate_edition) {
75         feature.set(&mut features, DUMMY_SP);
76         edition_enabled_features.insert(feature.name, crate_edition);
77     }
78
79     // Process the edition umbrella feature-gates first, to ensure
80     // `edition_enabled_features` is completed before it's queried.
81     for attr in krate_attrs {
82         if !attr.check_name(sym::feature) {
83             continue;
84         }
85
86         let list = match attr.meta_item_list() {
87             Some(list) => list,
88             None => continue,
89         };
90
91         for mi in list {
92             if !mi.is_word() {
93                 continue;
94             }
95
96             let name = mi.name_or_empty();
97
98             let edition = ALL_EDITIONS.iter().find(|e| name == e.feature_name()).copied();
99             if let Some(edition) = edition {
100                 if edition <= crate_edition {
101                     continue;
102                 }
103
104                 for feature in active_features_up_to(edition) {
105                     // FIXME(Manishearth) there is currently no way to set
106                     // lib features by edition
107                     feature.set(&mut features, DUMMY_SP);
108                     edition_enabled_features.insert(feature.name, edition);
109                 }
110             }
111         }
112     }
113
114     for attr in krate_attrs {
115         if !attr.check_name(sym::feature) {
116             continue;
117         }
118
119         let list = match attr.meta_item_list() {
120             Some(list) => list,
121             None => continue,
122         };
123
124         let bad_input = |span| {
125             struct_span_err!(span_handler, span, E0556, "malformed `feature` attribute input")
126         };
127
128         for mi in list {
129             let name = match mi.ident() {
130                 Some(ident) if mi.is_word() => ident.name,
131                 Some(ident) => {
132                     bad_input(mi.span())
133                         .span_suggestion(
134                             mi.span(),
135                             "expected just one word",
136                             format!("{}", ident.name),
137                             Applicability::MaybeIncorrect,
138                         )
139                         .emit();
140                     continue;
141                 }
142                 None => {
143                     bad_input(mi.span()).span_label(mi.span(), "expected just one word").emit();
144                     continue;
145                 }
146             };
147
148             if let Some(edition) = edition_enabled_features.get(&name) {
149                 let msg =
150                     &format!("the feature `{}` is included in the Rust {} edition", name, edition);
151                 span_handler.struct_span_warn_with_code(mi.span(), msg, error_code!(E0705)).emit();
152                 continue;
153             }
154
155             if ALL_EDITIONS.iter().any(|e| name == e.feature_name()) {
156                 // Handled in the separate loop above.
157                 continue;
158             }
159
160             let removed = REMOVED_FEATURES.iter().find(|f| name == f.name);
161             let stable_removed = STABLE_REMOVED_FEATURES.iter().find(|f| name == f.name);
162             if let Some(Feature { state, .. }) = removed.or(stable_removed) {
163                 if let FeatureState::Removed { reason } | FeatureState::Stabilized { reason } =
164                     state
165                 {
166                     feature_removed(span_handler, mi.span(), *reason);
167                     continue;
168                 }
169             }
170
171             if let Some(Feature { since, .. }) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
172                 let since = Some(Symbol::intern(since));
173                 features.declared_lang_features.push((name, mi.span(), since));
174                 continue;
175             }
176
177             if let Some(allowed) = allow_features.as_ref() {
178                 if allowed.iter().find(|&f| name.as_str() == *f).is_none() {
179                     struct_span_err!(
180                         span_handler,
181                         mi.span(),
182                         E0725,
183                         "the feature `{}` is not in the list of allowed features",
184                         name
185                     )
186                     .emit();
187                     continue;
188                 }
189             }
190
191             if let Some(f) = ACTIVE_FEATURES.iter().find(|f| name == f.name) {
192                 f.set(&mut features, mi.span());
193                 features.declared_lang_features.push((name, mi.span(), None));
194                 continue;
195             }
196
197             features.declared_lib_features.push((name, mi.span()));
198         }
199     }
200
201     features
202 }
203
204 // `cfg_attr`-process the crate's attributes and compute the crate's features.
205 pub fn features(
206     mut krate: ast::Crate,
207     sess: &ParseSess,
208     edition: Edition,
209     allow_features: &Option<Vec<String>>,
210 ) -> (ast::Crate, Features) {
211     let features;
212     {
213         let mut strip_unconfigured = StripUnconfigured { sess, features: None };
214
215         let unconfigured_attrs = krate.attrs.clone();
216         let err_count = sess.span_diagnostic.err_count();
217         if let Some(attrs) = strip_unconfigured.configure(krate.attrs) {
218             krate.attrs = attrs;
219         } else {
220             // the entire crate is unconfigured
221             krate.attrs = Vec::new();
222             krate.module.items = Vec::new();
223             return (krate, Features::default());
224         }
225
226         features = get_features(&sess.span_diagnostic, &krate.attrs, edition, allow_features);
227
228         // Avoid reconfiguring malformed `cfg_attr`s
229         if err_count == sess.span_diagnostic.err_count() {
230             strip_unconfigured.features = Some(&features);
231             strip_unconfigured.configure(unconfigured_attrs);
232         }
233     }
234
235     (krate, features)
236 }
237
238 #[macro_export]
239 macro_rules! configure {
240     ($this:ident, $node:ident) => {
241         match $this.configure($node) {
242             Some(node) => node,
243             None => return Default::default(),
244         }
245     };
246 }
247
248 const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]";
249 const CFG_ATTR_NOTE_REF: &str = "for more information, visit \
250     <https://doc.rust-lang.org/reference/conditional-compilation.html\
251     #the-cfg_attr-attribute>";
252
253 impl<'a> StripUnconfigured<'a> {
254     pub fn configure<T: HasAttrs>(&mut self, mut node: T) -> Option<T> {
255         self.process_cfg_attrs(&mut node);
256         self.in_cfg(node.attrs()).then_some(node)
257     }
258
259     /// Parse and expand all `cfg_attr` attributes into a list of attributes
260     /// that are within each `cfg_attr` that has a true configuration predicate.
261     ///
262     /// Gives compiler warnigns if any `cfg_attr` does not contain any
263     /// attributes and is in the original source code. Gives compiler errors if
264     /// the syntax of any `cfg_attr` is incorrect.
265     pub fn process_cfg_attrs<T: HasAttrs>(&mut self, node: &mut T) {
266         node.visit_attrs(|attrs| {
267             attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr));
268         });
269     }
270
271     /// Parse and expand a single `cfg_attr` attribute into a list of attributes
272     /// when the configuration predicate is true, or otherwise expand into an
273     /// empty list of attributes.
274     ///
275     /// Gives a compiler warning when the `cfg_attr` contains no attributes and
276     /// is in the original source file. Gives a compiler error if the syntax of
277     /// the attribute is incorrect.
278     fn process_cfg_attr(&mut self, attr: Attribute) -> Vec<Attribute> {
279         if !attr.has_name(sym::cfg_attr) {
280             return vec![attr];
281         }
282
283         let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) {
284             None => return vec![],
285             Some(r) => r,
286         };
287
288         // Lint on zero attributes in source.
289         if expanded_attrs.is_empty() {
290             return vec![attr];
291         }
292
293         // At this point we know the attribute is considered used.
294         attr::mark_used(&attr);
295
296         if !attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
297             return vec![];
298         }
299
300         // We call `process_cfg_attr` recursively in case there's a
301         // `cfg_attr` inside of another `cfg_attr`. E.g.
302         //  `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
303         expanded_attrs
304             .into_iter()
305             .flat_map(|(item, span)| {
306                 let attr = attr::mk_attr_from_item(attr.style, item, span);
307                 self.process_cfg_attr(attr)
308             })
309             .collect()
310     }
311
312     fn parse_cfg_attr(&self, attr: &Attribute) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> {
313         match attr.get_normal_item().args {
314             ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => {
315                 let msg = "wrong `cfg_attr` delimiters";
316                 validate_attr::check_meta_bad_delim(self.sess, dspan, delim, msg);
317                 match parse_in(self.sess, tts.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
318                     Ok(r) => return Some(r),
319                     Err(mut e) => e
320                         .help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP))
321                         .note(CFG_ATTR_NOTE_REF)
322                         .emit(),
323                 }
324             }
325             _ => self.error_malformed_cfg_attr_missing(attr.span),
326         }
327         None
328     }
329
330     fn error_malformed_cfg_attr_missing(&self, span: Span) {
331         self.sess
332             .span_diagnostic
333             .struct_span_err(span, "malformed `cfg_attr` attribute input")
334             .span_suggestion(
335                 span,
336                 "missing condition and attribute",
337                 CFG_ATTR_GRAMMAR_HELP.to_string(),
338                 Applicability::HasPlaceholders,
339             )
340             .note(CFG_ATTR_NOTE_REF)
341             .emit();
342     }
343
344     /// Determines if a node with the given attributes should be included in this configuration.
345     pub fn in_cfg(&self, attrs: &[Attribute]) -> bool {
346         attrs.iter().all(|attr| {
347             if !is_cfg(attr) {
348                 return true;
349             }
350
351             let error = |span, msg, suggestion: &str| {
352                 let mut err = self.sess.span_diagnostic.struct_span_err(span, msg);
353                 if !suggestion.is_empty() {
354                     err.span_suggestion(
355                         span,
356                         "expected syntax is",
357                         suggestion.into(),
358                         Applicability::MaybeIncorrect,
359                     );
360                 }
361                 err.emit();
362                 true
363             };
364
365             let meta_item = match validate_attr::parse_meta(self.sess, attr) {
366                 Ok(meta_item) => meta_item,
367                 Err(mut err) => {
368                     err.emit();
369                     return true;
370                 }
371             };
372             let nested_meta_items = if let Some(nested_meta_items) = meta_item.meta_item_list() {
373                 nested_meta_items
374             } else {
375                 return error(
376                     meta_item.span,
377                     "`cfg` is not followed by parentheses",
378                     "cfg(/* predicate */)",
379                 );
380             };
381
382             if nested_meta_items.is_empty() {
383                 return error(meta_item.span, "`cfg` predicate is not specified", "");
384             } else if nested_meta_items.len() > 1 {
385                 return error(
386                     nested_meta_items.last().unwrap().span(),
387                     "multiple `cfg` predicates are specified",
388                     "",
389                 );
390             }
391
392             match nested_meta_items[0].meta_item() {
393                 Some(meta_item) => attr::cfg_matches(meta_item, self.sess, self.features),
394                 None => error(
395                     nested_meta_items[0].span(),
396                     "`cfg` predicate key cannot be a literal",
397                     "",
398                 ),
399             }
400         })
401     }
402
403     /// Visit attributes on expression and statements (but not attributes on items in blocks).
404     fn visit_expr_attrs(&mut self, attrs: &[Attribute]) {
405         // flag the offending attributes
406         for attr in attrs.iter() {
407             self.maybe_emit_expr_attr_err(attr);
408         }
409     }
410
411     /// If attributes are not allowed on expressions, emit an error for `attr`
412     pub fn maybe_emit_expr_attr_err(&self, attr: &Attribute) {
413         if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) {
414             let mut err = feature_err(
415                 self.sess,
416                 sym::stmt_expr_attributes,
417                 attr.span,
418                 "attributes on expressions are experimental",
419             );
420
421             if attr.is_doc_comment() {
422                 err.help("`///` is for documentation comments. For a plain comment, use `//`.");
423             }
424
425             err.emit();
426         }
427     }
428
429     pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) {
430         let ast::ForeignMod { abi: _, items } = foreign_mod;
431         items.flat_map_in_place(|item| self.configure(item));
432     }
433
434     pub fn configure_generic_params(&mut self, params: &mut Vec<ast::GenericParam>) {
435         params.flat_map_in_place(|param| self.configure(param));
436     }
437
438     fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) {
439         match vdata {
440             ast::VariantData::Struct(fields, ..) | ast::VariantData::Tuple(fields, _) => {
441                 fields.flat_map_in_place(|field| self.configure(field))
442             }
443             ast::VariantData::Unit(_) => {}
444         }
445     }
446
447     pub fn configure_item_kind(&mut self, item: &mut ast::ItemKind) {
448         match item {
449             ast::ItemKind::Struct(def, _generics) | ast::ItemKind::Union(def, _generics) => {
450                 self.configure_variant_data(def)
451             }
452             ast::ItemKind::Enum(ast::EnumDef { variants }, _generics) => {
453                 variants.flat_map_in_place(|variant| self.configure(variant));
454                 for variant in variants {
455                     self.configure_variant_data(&mut variant.data);
456                 }
457             }
458             _ => {}
459         }
460     }
461
462     pub fn configure_expr_kind(&mut self, expr_kind: &mut ast::ExprKind) {
463         match expr_kind {
464             ast::ExprKind::Match(_m, arms) => {
465                 arms.flat_map_in_place(|arm| self.configure(arm));
466             }
467             ast::ExprKind::Struct(_path, fields, _base) => {
468                 fields.flat_map_in_place(|field| self.configure(field));
469             }
470             _ => {}
471         }
472     }
473
474     pub fn configure_expr(&mut self, expr: &mut P<ast::Expr>) {
475         self.visit_expr_attrs(expr.attrs());
476
477         // If an expr is valid to cfg away it will have been removed by the
478         // outer stmt or expression folder before descending in here.
479         // Anything else is always required, and thus has to error out
480         // in case of a cfg attr.
481         //
482         // N.B., this is intentionally not part of the visit_expr() function
483         //     in order for filter_map_expr() to be able to avoid this check
484         if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) {
485             let msg = "removing an expression is not supported in this position";
486             self.sess.span_diagnostic.span_err(attr.span, msg);
487         }
488
489         self.process_cfg_attrs(expr)
490     }
491
492     pub fn configure_pat(&mut self, pat: &mut P<ast::Pat>) {
493         if let ast::PatKind::Struct(_path, fields, _etc) = &mut pat.kind {
494             fields.flat_map_in_place(|field| self.configure(field));
495         }
496     }
497
498     pub fn configure_fn_decl(&mut self, fn_decl: &mut ast::FnDecl) {
499         fn_decl.inputs.flat_map_in_place(|arg| self.configure(arg));
500     }
501 }
502
503 impl<'a> MutVisitor for StripUnconfigured<'a> {
504     fn visit_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) {
505         self.configure_foreign_mod(foreign_mod);
506         noop_visit_foreign_mod(foreign_mod, self);
507     }
508
509     fn visit_item_kind(&mut self, item: &mut ast::ItemKind) {
510         self.configure_item_kind(item);
511         noop_visit_item_kind(item, self);
512     }
513
514     fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
515         self.configure_expr(expr);
516         self.configure_expr_kind(&mut expr.kind);
517         noop_visit_expr(expr, self);
518     }
519
520     fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
521         let mut expr = configure!(self, expr);
522         self.configure_expr_kind(&mut expr.kind);
523         noop_visit_expr(&mut expr, self);
524         Some(expr)
525     }
526
527     fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
528         noop_flat_map_stmt(configure!(self, stmt), self)
529     }
530
531     fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
532         noop_flat_map_item(configure!(self, item), self)
533     }
534
535     fn flat_map_impl_item(&mut self, item: ast::AssocItem) -> SmallVec<[ast::AssocItem; 1]> {
536         noop_flat_map_assoc_item(configure!(self, item), self)
537     }
538
539     fn flat_map_trait_item(&mut self, item: ast::AssocItem) -> SmallVec<[ast::AssocItem; 1]> {
540         noop_flat_map_assoc_item(configure!(self, item), self)
541     }
542
543     fn visit_mac(&mut self, _mac: &mut ast::Mac) {
544         // Don't configure interpolated AST (cf. issue #34171).
545         // Interpolated AST will get configured once the surrounding tokens are parsed.
546     }
547
548     fn visit_pat(&mut self, pat: &mut P<ast::Pat>) {
549         self.configure_pat(pat);
550         noop_visit_pat(pat, self)
551     }
552
553     fn visit_fn_decl(&mut self, mut fn_decl: &mut P<ast::FnDecl>) {
554         self.configure_fn_decl(&mut fn_decl);
555         noop_visit_fn_decl(fn_decl, self);
556     }
557 }
558
559 fn is_cfg(attr: &Attribute) -> bool {
560     attr.check_name(sym::cfg)
561 }
562
563 /// Process the potential `cfg` attributes on a module.
564 /// Also determine if the module should be included in this configuration.
565 pub fn process_configure_mod(
566     sess: &ParseSess,
567     cfg_mods: bool,
568     attrs: &[Attribute],
569 ) -> (bool, Vec<Attribute>) {
570     // Don't perform gated feature checking.
571     let mut strip_unconfigured = StripUnconfigured { sess, features: None };
572     let mut attrs = attrs.to_owned();
573     strip_unconfigured.process_cfg_attrs(&mut attrs);
574     (!cfg_mods || strip_unconfigured.in_cfg(&attrs), attrs)
575 }