]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_ast_passes/src/feature_gate.rs
Rollup merge of #104952 - jyn514:setup, r=Mark-Simulacrum
[rust.git] / compiler / rustc_ast_passes / src / feature_gate.rs
1 use rustc_ast as ast;
2 use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
3 use rustc_ast::{AssocConstraint, AssocConstraintKind, NodeId};
4 use rustc_ast::{PatKind, RangeEnd};
5 use rustc_errors::{struct_span_err, Applicability, StashKey};
6 use rustc_feature::{AttributeGate, BuiltinAttribute, Features, GateIssue, BUILTIN_ATTRIBUTE_MAP};
7 use rustc_session::parse::{feature_err, feature_err_issue, feature_warn};
8 use rustc_session::Session;
9 use rustc_span::source_map::Spanned;
10 use rustc_span::symbol::sym;
11 use rustc_span::Span;
12 use rustc_target::spec::abi;
13
14 macro_rules! gate_feature_fn {
15     ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $help: expr) => {{
16         let (visitor, has_feature, span, name, explain, help) =
17             (&*$visitor, $has_feature, $span, $name, $explain, $help);
18         let has_feature: bool = has_feature(visitor.features);
19         debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
20         if !has_feature && !span.allows_unstable($name) {
21             feature_err(&visitor.sess.parse_sess, name, span, explain).help(help).emit();
22         }
23     }};
24     ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
25         let (visitor, has_feature, span, name, explain) =
26             (&*$visitor, $has_feature, $span, $name, $explain);
27         let has_feature: bool = has_feature(visitor.features);
28         debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
29         if !has_feature && !span.allows_unstable($name) {
30             feature_err(&visitor.sess.parse_sess, name, span, explain).emit();
31         }
32     }};
33     (future_incompatible; $visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
34         let (visitor, has_feature, span, name, explain) =
35             (&*$visitor, $has_feature, $span, $name, $explain);
36         let has_feature: bool = has_feature(visitor.features);
37         debug!(
38             "gate_feature(feature = {:?}, span = {:?}); has? {} (future_incompatible)",
39             name, span, has_feature
40         );
41         if !has_feature && !span.allows_unstable($name) {
42             feature_warn(&visitor.sess.parse_sess, name, span, explain);
43         }
44     }};
45 }
46
47 macro_rules! gate_feature_post {
48     ($visitor: expr, $feature: ident, $span: expr, $explain: expr, $help: expr) => {
49         gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain, $help)
50     };
51     ($visitor: expr, $feature: ident, $span: expr, $explain: expr) => {
52         gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain)
53     };
54     (future_incompatible; $visitor: expr, $feature: ident, $span: expr, $explain: expr) => {
55         gate_feature_fn!(future_incompatible; $visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain)
56     };
57 }
58
59 pub fn check_attribute(attr: &ast::Attribute, sess: &Session, features: &Features) {
60     PostExpansionVisitor { sess, features }.visit_attribute(attr)
61 }
62
63 struct PostExpansionVisitor<'a> {
64     sess: &'a Session,
65
66     // `sess` contains a `Features`, but this might not be that one.
67     features: &'a Features,
68 }
69
70 impl<'a> PostExpansionVisitor<'a> {
71     fn check_abi(&self, abi: ast::StrLit, constness: ast::Const) {
72         let ast::StrLit { symbol_unescaped, span, .. } = abi;
73
74         if let ast::Const::Yes(_) = constness {
75             match symbol_unescaped {
76                 // Stable
77                 sym::Rust | sym::C => {}
78                 abi => gate_feature_post!(
79                     &self,
80                     const_extern_fn,
81                     span,
82                     &format!("`{}` as a `const fn` ABI is unstable", abi)
83                 ),
84             }
85         }
86
87         match abi::is_enabled(&self.features, span, symbol_unescaped.as_str()) {
88             Ok(()) => (),
89             Err(abi::AbiDisabled::Unstable { feature, explain }) => {
90                 feature_err_issue(
91                     &self.sess.parse_sess,
92                     feature,
93                     span,
94                     GateIssue::Language,
95                     explain,
96                 )
97                 .emit();
98             }
99             Err(abi::AbiDisabled::Unrecognized) => {
100                 if self.sess.opts.pretty.map_or(true, |ppm| ppm.needs_hir()) {
101                     self.sess.parse_sess.span_diagnostic.delay_span_bug(
102                         span,
103                         &format!(
104                             "unrecognized ABI not caught in lowering: {}",
105                             symbol_unescaped.as_str()
106                         ),
107                     );
108                 }
109             }
110         }
111     }
112
113     fn check_extern(&self, ext: ast::Extern, constness: ast::Const) {
114         if let ast::Extern::Explicit(abi, _) = ext {
115             self.check_abi(abi, constness);
116         }
117     }
118
119     /// Feature gate `impl Trait` inside `type Alias = $type_expr;`.
120     fn check_impl_trait(&self, ty: &ast::Ty) {
121         struct ImplTraitVisitor<'a> {
122             vis: &'a PostExpansionVisitor<'a>,
123         }
124         impl Visitor<'_> for ImplTraitVisitor<'_> {
125             fn visit_ty(&mut self, ty: &ast::Ty) {
126                 if let ast::TyKind::ImplTrait(..) = ty.kind {
127                     gate_feature_post!(
128                         &self.vis,
129                         type_alias_impl_trait,
130                         ty.span,
131                         "`impl Trait` in type aliases is unstable"
132                     );
133                 }
134                 visit::walk_ty(self, ty);
135             }
136         }
137         ImplTraitVisitor { vis: self }.visit_ty(ty);
138     }
139 }
140
141 impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
142     fn visit_attribute(&mut self, attr: &ast::Attribute) {
143         let attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
144         // Check feature gates for built-in attributes.
145         if let Some(BuiltinAttribute {
146             gate: AttributeGate::Gated(_, name, descr, has_feature),
147             ..
148         }) = attr_info
149         {
150             gate_feature_fn!(self, has_feature, attr.span, *name, descr);
151         }
152         // Check unstable flavors of the `#[doc]` attribute.
153         if attr.has_name(sym::doc) {
154             for nested_meta in attr.meta_item_list().unwrap_or_default() {
155                 macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => {
156                     $(if nested_meta.has_name(sym::$name) {
157                         let msg = concat!("`#[doc(", stringify!($name), ")]` is experimental");
158                         gate_feature_post!(self, $feature, attr.span, msg);
159                     })*
160                 }}
161
162                 gate_doc!(
163                     cfg => doc_cfg
164                     cfg_hide => doc_cfg_hide
165                     masked => doc_masked
166                     notable_trait => doc_notable_trait
167                 );
168
169                 if nested_meta.has_name(sym::keyword) {
170                     let msg = "`#[doc(keyword)]` is meant for internal use only";
171                     gate_feature_post!(self, rustdoc_internals, attr.span, msg);
172                 }
173
174                 if nested_meta.has_name(sym::fake_variadic) {
175                     let msg = "`#[doc(fake_variadic)]` is meant for internal use only";
176                     gate_feature_post!(self, rustdoc_internals, attr.span, msg);
177                 }
178             }
179         }
180
181         // Emit errors for non-staged-api crates.
182         if !self.features.staged_api {
183             if attr.has_name(sym::unstable)
184                 || attr.has_name(sym::stable)
185                 || attr.has_name(sym::rustc_const_unstable)
186                 || attr.has_name(sym::rustc_const_stable)
187                 || attr.has_name(sym::rustc_default_body_unstable)
188             {
189                 struct_span_err!(
190                     self.sess,
191                     attr.span,
192                     E0734,
193                     "stability attributes may not be used outside of the standard library",
194                 )
195                 .emit();
196             }
197         }
198     }
199
200     fn visit_item(&mut self, i: &'a ast::Item) {
201         match &i.kind {
202             ast::ItemKind::ForeignMod(foreign_module) => {
203                 if let Some(abi) = foreign_module.abi {
204                     self.check_abi(abi, ast::Const::No);
205                 }
206             }
207
208             ast::ItemKind::Fn(..) => {
209                 if self.sess.contains_name(&i.attrs, sym::start) {
210                     gate_feature_post!(
211                         &self,
212                         start,
213                         i.span,
214                         "`#[start]` functions are experimental \
215                          and their signature may change \
216                          over time"
217                     );
218                 }
219             }
220
221             ast::ItemKind::Struct(..) => {
222                 for attr in self.sess.filter_by_name(&i.attrs, sym::repr) {
223                     for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
224                         if item.has_name(sym::simd) {
225                             gate_feature_post!(
226                                 &self,
227                                 repr_simd,
228                                 attr.span,
229                                 "SIMD types are experimental and possibly buggy"
230                             );
231                         }
232                     }
233                 }
234             }
235
236             ast::ItemKind::Impl(box ast::Impl { polarity, defaultness, of_trait, .. }) => {
237                 if let &ast::ImplPolarity::Negative(span) = polarity {
238                     gate_feature_post!(
239                         &self,
240                         negative_impls,
241                         span.to(of_trait.as_ref().map_or(span, |t| t.path.span)),
242                         "negative trait bounds are not yet fully implemented; \
243                          use marker types for now"
244                     );
245                 }
246
247                 if let ast::Defaultness::Default(_) = defaultness {
248                     gate_feature_post!(&self, specialization, i.span, "specialization is unstable");
249                 }
250             }
251
252             ast::ItemKind::Trait(box ast::Trait { is_auto: ast::IsAuto::Yes, .. }) => {
253                 gate_feature_post!(
254                     &self,
255                     auto_traits,
256                     i.span,
257                     "auto traits are experimental and possibly buggy"
258                 );
259             }
260
261             ast::ItemKind::TraitAlias(..) => {
262                 gate_feature_post!(&self, trait_alias, i.span, "trait aliases are experimental");
263             }
264
265             ast::ItemKind::MacroDef(ast::MacroDef { macro_rules: false, .. }) => {
266                 let msg = "`macro` is experimental";
267                 gate_feature_post!(&self, decl_macro, i.span, msg);
268             }
269
270             ast::ItemKind::TyAlias(box ast::TyAlias { ty: Some(ty), .. }) => {
271                 self.check_impl_trait(&ty)
272             }
273
274             _ => {}
275         }
276
277         visit::walk_item(self, i);
278     }
279
280     fn visit_foreign_item(&mut self, i: &'a ast::ForeignItem) {
281         match i.kind {
282             ast::ForeignItemKind::Fn(..) | ast::ForeignItemKind::Static(..) => {
283                 let link_name = self.sess.first_attr_value_str_by_name(&i.attrs, sym::link_name);
284                 let links_to_llvm =
285                     link_name.map_or(false, |val| val.as_str().starts_with("llvm."));
286                 if links_to_llvm {
287                     gate_feature_post!(
288                         &self,
289                         link_llvm_intrinsics,
290                         i.span,
291                         "linking to LLVM intrinsics is experimental"
292                     );
293                 }
294             }
295             ast::ForeignItemKind::TyAlias(..) => {
296                 gate_feature_post!(&self, extern_types, i.span, "extern types are experimental");
297             }
298             ast::ForeignItemKind::MacCall(..) => {}
299         }
300
301         visit::walk_foreign_item(self, i)
302     }
303
304     fn visit_ty(&mut self, ty: &'a ast::Ty) {
305         match &ty.kind {
306             ast::TyKind::BareFn(bare_fn_ty) => {
307                 // Function pointers cannot be `const`
308                 self.check_extern(bare_fn_ty.ext, ast::Const::No);
309             }
310             ast::TyKind::Never => {
311                 gate_feature_post!(&self, never_type, ty.span, "the `!` type is experimental");
312             }
313             ast::TyKind::TraitObject(_, ast::TraitObjectSyntax::DynStar, ..) => {
314                 gate_feature_post!(&self, dyn_star, ty.span, "dyn* trait objects are unstable");
315             }
316             _ => {}
317         }
318         visit::walk_ty(self, ty)
319     }
320
321     fn visit_fn_ret_ty(&mut self, ret_ty: &'a ast::FnRetTy) {
322         if let ast::FnRetTy::Ty(output_ty) = ret_ty {
323             if let ast::TyKind::Never = output_ty.kind {
324                 // Do nothing.
325             } else {
326                 self.visit_ty(output_ty)
327             }
328         }
329     }
330
331     fn visit_stmt(&mut self, stmt: &'a ast::Stmt) {
332         if let ast::StmtKind::Semi(expr) = &stmt.kind
333             && let ast::ExprKind::Assign(lhs, _, _) = &expr.kind
334             && let ast::ExprKind::Type(..) = lhs.kind
335             && self.sess.parse_sess.span_diagnostic.err_count() == 0
336             && !self.features.type_ascription
337             && !lhs.span.allows_unstable(sym::type_ascription)
338         {
339             // When we encounter a statement of the form `foo: Ty = val;`, this will emit a type
340             // ascription error, but the likely intention was to write a `let` statement. (#78907).
341             feature_err(
342                 &self.sess.parse_sess,
343                 sym::type_ascription,
344                 lhs.span,
345                 "type ascription is experimental",
346             ).span_suggestion_verbose(
347                 lhs.span.shrink_to_lo(),
348                 "you might have meant to introduce a new binding",
349                 "let ".to_string(),
350                 Applicability::MachineApplicable,
351             ).emit();
352         }
353         visit::walk_stmt(self, stmt);
354     }
355
356     fn visit_expr(&mut self, e: &'a ast::Expr) {
357         match e.kind {
358             ast::ExprKind::Box(_) => {
359                 gate_feature_post!(
360                     &self,
361                     box_syntax,
362                     e.span,
363                     "box expression syntax is experimental; you can call `Box::new` instead"
364                 );
365             }
366             ast::ExprKind::Type(..) => {
367                 if self.sess.parse_sess.span_diagnostic.err_count() == 0 {
368                     // To avoid noise about type ascription in common syntax errors,
369                     // only emit if it is the *only* error.
370                     gate_feature_post!(
371                         &self,
372                         type_ascription,
373                         e.span,
374                         "type ascription is experimental"
375                     );
376                 } else {
377                     // And if it isn't, cancel the early-pass warning.
378                     self.sess
379                         .parse_sess
380                         .span_diagnostic
381                         .steal_diagnostic(e.span, StashKey::EarlySyntaxWarning)
382                         .map(|err| err.cancel());
383                 }
384             }
385             ast::ExprKind::TryBlock(_) => {
386                 gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
387             }
388             _ => {}
389         }
390         visit::walk_expr(self, e)
391     }
392
393     fn visit_pat(&mut self, pattern: &'a ast::Pat) {
394         match &pattern.kind {
395             PatKind::Slice(pats) => {
396                 for pat in pats {
397                     let inner_pat = match &pat.kind {
398                         PatKind::Ident(.., Some(pat)) => pat,
399                         _ => pat,
400                     };
401                     if let PatKind::Range(Some(_), None, Spanned { .. }) = inner_pat.kind {
402                         gate_feature_post!(
403                             &self,
404                             half_open_range_patterns_in_slices,
405                             pat.span,
406                             "`X..` patterns in slices are experimental"
407                         );
408                     }
409                 }
410             }
411             PatKind::Box(..) => {
412                 gate_feature_post!(
413                     &self,
414                     box_patterns,
415                     pattern.span,
416                     "box pattern syntax is experimental"
417                 );
418             }
419             PatKind::Range(_, Some(_), Spanned { node: RangeEnd::Excluded, .. }) => {
420                 gate_feature_post!(
421                     &self,
422                     exclusive_range_pattern,
423                     pattern.span,
424                     "exclusive range pattern syntax is experimental"
425                 );
426             }
427             _ => {}
428         }
429         visit::walk_pat(self, pattern)
430     }
431
432     fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
433         if let Some(header) = fn_kind.header() {
434             // Stability of const fn methods are covered in `visit_assoc_item` below.
435             self.check_extern(header.ext, header.constness);
436         }
437
438         if fn_kind.ctxt() != Some(FnCtxt::Foreign) && fn_kind.decl().c_variadic() {
439             gate_feature_post!(&self, c_variadic, span, "C-variadic functions are unstable");
440         }
441
442         visit::walk_fn(self, fn_kind)
443     }
444
445     fn visit_assoc_constraint(&mut self, constraint: &'a AssocConstraint) {
446         if let AssocConstraintKind::Bound { .. } = constraint.kind {
447             gate_feature_post!(
448                 &self,
449                 associated_type_bounds,
450                 constraint.span,
451                 "associated type bounds are unstable"
452             )
453         }
454         visit::walk_assoc_constraint(self, constraint)
455     }
456
457     fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) {
458         let is_fn = match &i.kind {
459             ast::AssocItemKind::Fn(_) => true,
460             ast::AssocItemKind::Type(box ast::TyAlias { ty, .. }) => {
461                 if let (Some(_), AssocCtxt::Trait) = (ty, ctxt) {
462                     gate_feature_post!(
463                         &self,
464                         associated_type_defaults,
465                         i.span,
466                         "associated type defaults are unstable"
467                     );
468                 }
469                 if let Some(ty) = ty {
470                     self.check_impl_trait(ty);
471                 }
472                 false
473             }
474             _ => false,
475         };
476         if let ast::Defaultness::Default(_) = i.kind.defaultness() {
477             // Limit `min_specialization` to only specializing functions.
478             gate_feature_fn!(
479                 &self,
480                 |x: &Features| x.specialization || (is_fn && x.min_specialization),
481                 i.span,
482                 sym::specialization,
483                 "specialization is unstable"
484             );
485         }
486         visit::walk_assoc_item(self, i, ctxt)
487     }
488 }
489
490 pub fn check_crate(krate: &ast::Crate, sess: &Session) {
491     maybe_stage_features(sess, krate);
492     check_incompatible_features(sess);
493     let mut visitor = PostExpansionVisitor { sess, features: &sess.features_untracked() };
494
495     let spans = sess.parse_sess.gated_spans.spans.borrow();
496     macro_rules! gate_all {
497         ($gate:ident, $msg:literal, $help:literal) => {
498             if let Some(spans) = spans.get(&sym::$gate) {
499                 for span in spans {
500                     gate_feature_post!(&visitor, $gate, *span, $msg, $help);
501                 }
502             }
503         };
504         ($gate:ident, $msg:literal) => {
505             if let Some(spans) = spans.get(&sym::$gate) {
506                 for span in spans {
507                     gate_feature_post!(&visitor, $gate, *span, $msg);
508                 }
509             }
510         };
511     }
512     gate_all!(
513         if_let_guard,
514         "`if let` guards are experimental",
515         "you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`"
516     );
517     gate_all!(let_chains, "`let` expressions in this position are unstable");
518     gate_all!(
519         async_closure,
520         "async closures are unstable",
521         "to use an async block, remove the `||`: `async {`"
522     );
523     gate_all!(
524         closure_lifetime_binder,
525         "`for<...>` binders for closures are experimental",
526         "consider removing `for<...>`"
527     );
528     gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental");
529     gate_all!(generators, "yield syntax is experimental");
530     gate_all!(raw_ref_op, "raw address of syntax is experimental");
531     gate_all!(const_trait_impl, "const trait impls are experimental");
532     gate_all!(
533         half_open_range_patterns_in_slices,
534         "half-open range patterns in slices are unstable"
535     );
536     gate_all!(inline_const, "inline-const is experimental");
537     gate_all!(inline_const_pat, "inline-const in pattern position is experimental");
538     gate_all!(associated_const_equality, "associated const equality is incomplete");
539     gate_all!(yeet_expr, "`do yeet` expression is experimental");
540
541     // All uses of `gate_all!` below this point were added in #65742,
542     // and subsequently disabled (with the non-early gating readded).
543     // We emit an early future-incompatible warning for these.
544     // New syntax gates should go above here to get a hard error gate.
545     macro_rules! gate_all {
546         ($gate:ident, $msg:literal) => {
547             for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
548                 gate_feature_post!(future_incompatible; &visitor, $gate, *span, $msg);
549             }
550         };
551     }
552
553     gate_all!(trait_alias, "trait aliases are experimental");
554     gate_all!(associated_type_bounds, "associated type bounds are unstable");
555     gate_all!(decl_macro, "`macro` is experimental");
556     gate_all!(box_patterns, "box pattern syntax is experimental");
557     gate_all!(exclusive_range_pattern, "exclusive range pattern syntax is experimental");
558     gate_all!(try_blocks, "`try` blocks are unstable");
559     gate_all!(box_syntax, "box expression syntax is experimental; you can call `Box::new` instead");
560     gate_all!(type_ascription, "type ascription is experimental");
561
562     visit::walk_crate(&mut visitor, krate);
563 }
564
565 fn maybe_stage_features(sess: &Session, krate: &ast::Crate) {
566     // checks if `#![feature]` has been used to enable any lang feature
567     // does not check the same for lib features unless there's at least one
568     // declared lang feature
569     if !sess.opts.unstable_features.is_nightly_build() {
570         let lang_features = &sess.features_untracked().declared_lang_features;
571         if lang_features.len() == 0 {
572             return;
573         }
574         for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) {
575             let mut err = struct_span_err!(
576                 sess.parse_sess.span_diagnostic,
577                 attr.span,
578                 E0554,
579                 "`#![feature]` may not be used on the {} release channel",
580                 option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)")
581             );
582             let mut all_stable = true;
583             for ident in
584                 attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident())
585             {
586                 let name = ident.name;
587                 let stable_since = lang_features
588                     .iter()
589                     .flat_map(|&(feature, _, since)| if feature == name { since } else { None })
590                     .next();
591                 if let Some(since) = stable_since {
592                     err.help(&format!(
593                         "the feature `{}` has been stable since {} and no longer requires \
594                                   an attribute to enable",
595                         name, since
596                     ));
597                 } else {
598                     all_stable = false;
599                 }
600             }
601             if all_stable {
602                 err.span_suggestion(
603                     attr.span,
604                     "remove the attribute",
605                     "",
606                     Applicability::MachineApplicable,
607                 );
608             }
609             err.emit();
610         }
611     }
612 }
613
614 fn check_incompatible_features(sess: &Session) {
615     let features = sess.features_untracked();
616
617     let declared_features = features
618         .declared_lang_features
619         .iter()
620         .copied()
621         .map(|(name, span, _)| (name, span))
622         .chain(features.declared_lib_features.iter().copied());
623
624     for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES
625         .iter()
626         .filter(|&&(f1, f2)| features.enabled(f1) && features.enabled(f2))
627     {
628         if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| name == f1) {
629             if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2)
630             {
631                 let spans = vec![f1_span, f2_span];
632                 sess.struct_span_err(
633                     spans.clone(),
634                     &format!(
635                         "features `{}` and `{}` are incompatible, using them at the same time \
636                         is not allowed",
637                         f1_name, f2_name
638                     ),
639                 )
640                 .help("remove one of these features")
641                 .emit();
642             }
643         }
644     }
645 }