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