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