2 use rustc_ast
::visit
::{self, AssocCtxt, FnCtxt, FnKind, Visitor}
;
3 use rustc_ast
::{AssocConstraint, AssocConstraintKind, NodeId}
;
4 use rustc_ast
::{PatKind, RangeEnd, VariantData}
;
5 use rustc_errors
::struct_span_err
;
6 use rustc_feature
::{AttributeGate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}
;
7 use rustc_feature
::{Features, GateIssue}
;
8 use rustc_session
::parse
::{feature_err, feature_err_issue}
;
9 use rustc_session
::Session
;
10 use rustc_span
::source_map
::Spanned
;
11 use rustc_span
::symbol
::sym
;
16 macro_rules
! gate_feature_fn
{
17 ($visitor
: expr
, $has_feature
: expr
, $span
: expr
, $name
: expr
, $explain
: expr
, $help
: expr
) => {{
18 let (visitor
, has_feature
, span
, name
, explain
, help
) =
19 (&*$visitor
, $has_feature
, $span
, $name
, $explain
, $help
);
20 let has_feature
: bool
= has_feature(visitor
.features
);
21 debug
!("gate_feature(feature = {:?}, span = {:?}); has? {}", name
, span
, has_feature
);
22 if !has_feature
&& !span
.allows_unstable($name
) {
23 feature_err_issue(&visitor
.sess
.parse_sess
, name
, span
, GateIssue
::Language
, explain
)
28 ($visitor
: expr
, $has_feature
: expr
, $span
: expr
, $name
: expr
, $explain
: expr
) => {{
29 let (visitor
, has_feature
, span
, name
, explain
) =
30 (&*$visitor
, $has_feature
, $span
, $name
, $explain
);
31 let has_feature
: bool
= has_feature(visitor
.features
);
32 debug
!("gate_feature(feature = {:?}, span = {:?}); has? {}", name
, span
, has_feature
);
33 if !has_feature
&& !span
.allows_unstable($name
) {
34 feature_err_issue(&visitor
.sess
.parse_sess
, name
, span
, GateIssue
::Language
, explain
)
40 macro_rules
! gate_feature_post
{
41 ($visitor
: expr
, $feature
: ident
, $span
: expr
, $explain
: expr
, $help
: expr
) => {
42 gate_feature_fn
!($visitor
, |x
: &Features
| x
.$feature
, $span
, sym
::$feature
, $explain
, $help
)
44 ($visitor
: expr
, $feature
: ident
, $span
: expr
, $explain
: expr
) => {
45 gate_feature_fn
!($visitor
, |x
: &Features
| x
.$feature
, $span
, sym
::$feature
, $explain
)
49 pub fn check_attribute(attr
: &ast
::Attribute
, sess
: &Session
, features
: &Features
) {
50 PostExpansionVisitor { sess, features }
.visit_attribute(attr
)
53 struct PostExpansionVisitor
<'a
> {
56 // `sess` contains a `Features`, but this might not be that one.
57 features
: &'a Features
,
60 impl<'a
> PostExpansionVisitor
<'a
> {
61 fn check_abi(&self, abi
: ast
::StrLit
, constness
: ast
::Const
) {
62 let ast
::StrLit { symbol_unescaped, span, .. }
= abi
;
64 if let ast
::Const
::Yes(_
) = constness
{
65 match symbol_unescaped
.as_str() {
68 abi
=> gate_feature_post
!(
72 &format
!("`{}` as a `const fn` ABI is unstable", abi
)
77 match symbol_unescaped
.as_str() {
79 "Rust" | "C" | "cdecl" | "stdcall" | "fastcall" | "aapcs" | "win64" | "sysv64"
82 gate_feature_post
!(&self, intrinsics
, span
, "intrinsics are subject to change");
84 "platform-intrinsic" => {
89 "platform intrinsics are experimental and possibly buggy"
97 "vectorcall is experimental and subject to change"
105 "thiscall is experimental and subject to change"
113 "rust-call ABI is subject to change"
121 "rust-cold is experimental and subject to change"
129 "PTX ABIs are experimental and subject to change"
137 "unadjusted ABI is an implementation detail and perma-unstable"
140 "msp430-interrupt" => {
143 abi_msp430_interrupt
,
145 "msp430-interrupt ABI is experimental and subject to change"
153 "x86-interrupt ABI is experimental and subject to change"
161 "amdgpu-kernel ABI is experimental and subject to change"
164 "avr-interrupt" | "avr-non-blocking-interrupt" => {
169 "avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change"
177 "efiapi ABI is experimental and subject to change"
180 "C-cmse-nonsecure-call" => {
183 abi_c_cmse_nonsecure_call
,
185 "C-cmse-nonsecure-call ABI is experimental and subject to change"
193 "C-unwind ABI is experimental and subject to change"
196 "stdcall-unwind" => {
201 "stdcall-unwind ABI is experimental and subject to change"
209 "system-unwind ABI is experimental and subject to change"
212 "thiscall-unwind" => {
217 "thiscall-unwind ABI is experimental and subject to change"
225 "cdecl-unwind ABI is experimental and subject to change"
228 "fastcall-unwind" => {
233 "fastcall-unwind ABI is experimental and subject to change"
236 "vectorcall-unwind" => {
241 "vectorcall-unwind ABI is experimental and subject to change"
249 "aapcs-unwind ABI is experimental and subject to change"
257 "win64-unwind ABI is experimental and subject to change"
265 "sysv64-unwind ABI is experimental and subject to change"
273 "wasm ABI is experimental and subject to change"
277 self.sess
.parse_sess
.span_diagnostic
.delay_span_bug(
279 &format
!("unrecognized ABI not caught in lowering: {}", abi
),
285 fn check_extern(&self, ext
: ast
::Extern
, constness
: ast
::Const
) {
286 if let ast
::Extern
::Explicit(abi
) = ext
{
287 self.check_abi(abi
, constness
);
291 fn maybe_report_invalid_custom_discriminants(&self, variants
: &[ast
::Variant
]) {
292 let has_fields
= variants
.iter().any(|variant
| match variant
.data
{
293 VariantData
::Tuple(..) | VariantData
::Struct(..) => true,
294 VariantData
::Unit(..) => false,
297 let discriminant_spans
= variants
299 .filter(|variant
| match variant
.data
{
300 VariantData
::Tuple(..) | VariantData
::Struct(..) => false,
301 VariantData
::Unit(..) => true,
303 .filter_map(|variant
| variant
.disr_expr
.as_ref().map(|c
| c
.value
.span
))
304 .collect
::<Vec
<_
>>();
306 if !discriminant_spans
.is_empty() && has_fields
{
307 let mut err
= feature_err(
308 &self.sess
.parse_sess
,
309 sym
::arbitrary_enum_discriminant
,
310 discriminant_spans
.clone(),
311 "custom discriminant values are not allowed in enums with tuple or struct variants",
313 for sp
in discriminant_spans
{
314 err
.span_label(sp
, "disallowed custom discriminant");
316 for variant
in variants
.iter() {
317 match &variant
.data
{
318 VariantData
::Struct(..) => {
319 err
.span_label(variant
.span
, "struct variant defined here");
321 VariantData
::Tuple(..) => {
322 err
.span_label(variant
.span
, "tuple variant defined here");
324 VariantData
::Unit(..) => {}
331 fn check_gat(&self, generics
: &ast
::Generics
, span
: Span
) {
332 if !generics
.params
.is_empty() {
335 generic_associated_types
,
337 "generic associated types are unstable"
340 if !generics
.where_clause
.predicates
.is_empty() {
343 generic_associated_types
,
345 "where clauses on associated types are unstable"
350 /// Feature gate `impl Trait` inside `type Alias = $type_expr;`.
351 fn check_impl_trait(&self, ty
: &ast
::Ty
) {
352 struct ImplTraitVisitor
<'a
> {
353 vis
: &'a PostExpansionVisitor
<'a
>,
355 impl Visitor
<'_
> for ImplTraitVisitor
<'_
> {
356 fn visit_ty(&mut self, ty
: &ast
::Ty
) {
357 if let ast
::TyKind
::ImplTrait(..) = ty
.kind
{
360 type_alias_impl_trait
,
362 "`impl Trait` in type aliases is unstable"
365 visit
::walk_ty(self, ty
);
368 ImplTraitVisitor { vis: self }
.visit_ty(ty
);
372 impl<'a
> Visitor
<'a
> for PostExpansionVisitor
<'a
> {
373 fn visit_attribute(&mut self, attr
: &ast
::Attribute
) {
374 let attr_info
= attr
.ident().and_then(|ident
| BUILTIN_ATTRIBUTE_MAP
.get(&ident
.name
));
375 // Check feature gates for built-in attributes.
376 if let Some(BuiltinAttribute
{
377 gate
: AttributeGate
::Gated(_
, name
, descr
, has_feature
),
381 gate_feature_fn
!(self, has_feature
, attr
.span
, *name
, descr
);
383 // Check unstable flavors of the `#[doc]` attribute.
384 if attr
.has_name(sym
::doc
) {
385 for nested_meta
in attr
.meta_item_list().unwrap_or_default() {
386 macro_rules
! gate_doc
{ ($
($name
:ident
=> $feature
:ident
)*) => {
387 $
(if nested_meta
.has_name(sym
::$name
) {
388 let msg
= concat
!("`#[doc(", stringify
!($name
), ")]` is experimental");
389 gate_feature_post
!(self, $feature
, attr
.span
, msg
);
395 cfg_hide
=> doc_cfg_hide
397 notable_trait
=> doc_notable_trait
400 if nested_meta
.has_name(sym
::keyword
) {
401 let msg
= "`#[doc(keyword)]` is meant for internal use only";
402 gate_feature_post
!(self, rustdoc_internals
, attr
.span
, msg
);
405 if nested_meta
.has_name(sym
::tuple_variadic
) {
406 let msg
= "`#[doc(tuple_variadic)]` is meant for internal use only";
407 gate_feature_post
!(self, rustdoc_internals
, attr
.span
, msg
);
412 // Emit errors for non-staged-api crates.
413 if !self.features
.staged_api
{
414 if attr
.has_name(sym
::unstable
)
415 || attr
.has_name(sym
::stable
)
416 || attr
.has_name(sym
::rustc_const_unstable
)
417 || attr
.has_name(sym
::rustc_const_stable
)
423 "stability attributes may not be used outside of the standard library",
430 fn visit_item(&mut self, i
: &'a ast
::Item
) {
432 ast
::ItemKind
::ForeignMod(ref foreign_module
) => {
433 if let Some(abi
) = foreign_module
.abi
{
434 self.check_abi(abi
, ast
::Const
::No
);
438 ast
::ItemKind
::Fn(..) => {
439 if self.sess
.contains_name(&i
.attrs
, sym
::start
) {
444 "`#[start]` functions are experimental \
445 and their signature may change \
451 ast
::ItemKind
::Struct(..) => {
452 for attr
in self.sess
.filter_by_name(&i
.attrs
, sym
::repr
) {
453 for item
in attr
.meta_item_list().unwrap_or_else(Vec
::new
) {
454 if item
.has_name(sym
::simd
) {
459 "SIMD types are experimental and possibly buggy"
466 ast
::ItemKind
::Enum(ast
::EnumDef { ref variants, .. }
, ..) => {
467 for variant
in variants
{
468 match (&variant
.data
, &variant
.disr_expr
) {
469 (ast
::VariantData
::Unit(..), _
) => {}
470 (_
, Some(disr_expr
)) => gate_feature_post
!(
472 arbitrary_enum_discriminant
,
473 disr_expr
.value
.span
,
474 "discriminants on non-unit variants are experimental"
480 let has_feature
= self.features
.arbitrary_enum_discriminant
;
481 if !has_feature
&& !i
.span
.allows_unstable(sym
::arbitrary_enum_discriminant
) {
482 self.maybe_report_invalid_custom_discriminants(&variants
);
486 ast
::ItemKind
::Impl(box ast
::Impl { polarity, defaultness, ref of_trait, .. }
) => {
487 if let ast
::ImplPolarity
::Negative(span
) = polarity
{
491 span
.to(of_trait
.as_ref().map_or(span
, |t
| t
.path
.span
)),
492 "negative trait bounds are not yet fully implemented; \
493 use marker types for now"
497 if let ast
::Defaultness
::Default(_
) = defaultness
{
498 gate_feature_post
!(&self, specialization
, i
.span
, "specialization is unstable");
502 ast
::ItemKind
::Trait(box ast
::Trait { is_auto: ast::IsAuto::Yes, .. }
) => {
507 "auto traits are experimental and possibly buggy"
511 ast
::ItemKind
::TraitAlias(..) => {
512 gate_feature_post
!(&self, trait_alias
, i
.span
, "trait aliases are experimental");
515 ast
::ItemKind
::MacroDef(ast
::MacroDef { macro_rules: false, .. }
) => {
516 let msg
= "`macro` is experimental";
517 gate_feature_post
!(&self, decl_macro
, i
.span
, msg
);
520 ast
::ItemKind
::TyAlias(box ast
::TyAlias { ty: Some(ref ty), .. }
) => {
521 self.check_impl_trait(&ty
)
527 visit
::walk_item(self, i
);
530 fn visit_foreign_item(&mut self, i
: &'a ast
::ForeignItem
) {
532 ast
::ForeignItemKind
::Fn(..) | ast
::ForeignItemKind
::Static(..) => {
533 let link_name
= self.sess
.first_attr_value_str_by_name(&i
.attrs
, sym
::link_name
);
535 link_name
.map_or(false, |val
| val
.as_str().starts_with("llvm."));
539 link_llvm_intrinsics
,
541 "linking to LLVM intrinsics is experimental"
545 ast
::ForeignItemKind
::TyAlias(..) => {
546 gate_feature_post
!(&self, extern_types
, i
.span
, "extern types are experimental");
548 ast
::ForeignItemKind
::MacCall(..) => {}
551 visit
::walk_foreign_item(self, i
)
554 fn visit_ty(&mut self, ty
: &'a ast
::Ty
) {
556 ast
::TyKind
::BareFn(ref bare_fn_ty
) => {
557 // Function pointers cannot be `const`
558 self.check_extern(bare_fn_ty
.ext
, ast
::Const
::No
);
560 ast
::TyKind
::Never
=> {
561 gate_feature_post
!(&self, never_type
, ty
.span
, "the `!` type is experimental");
565 visit
::walk_ty(self, ty
)
568 fn visit_fn_ret_ty(&mut self, ret_ty
: &'a ast
::FnRetTy
) {
569 if let ast
::FnRetTy
::Ty(ref output_ty
) = *ret_ty
{
570 if let ast
::TyKind
::Never
= output_ty
.kind
{
573 self.visit_ty(output_ty
)
578 fn visit_expr(&mut self, e
: &'a ast
::Expr
) {
580 ast
::ExprKind
::Box(_
) => {
585 "box expression syntax is experimental; you can call `Box::new` instead"
588 ast
::ExprKind
::Type(..) => {
589 // To avoid noise about type ascription in common syntax errors, only emit if it
590 // is the *only* error.
591 if self.sess
.parse_sess
.span_diagnostic
.err_count() == 0 {
596 "type ascription is experimental"
600 ast
::ExprKind
::TryBlock(_
) => {
601 gate_feature_post
!(&self, try_blocks
, e
.span
, "`try` expression is experimental");
603 ast
::ExprKind
::Block(_
, Some(label
)) => {
608 "labels on blocks are unstable"
613 visit
::walk_expr(self, e
)
616 fn visit_pat(&mut self, pattern
: &'a ast
::Pat
) {
617 match &pattern
.kind
{
618 PatKind
::Slice(pats
) => {
620 let inner_pat
= match &pat
.kind
{
621 PatKind
::Ident(.., Some(pat
)) => pat
,
624 if let PatKind
::Range(Some(_
), None
, Spanned { .. }
) = inner_pat
.kind
{
627 half_open_range_patterns
,
629 "`X..` patterns in slices are experimental"
634 PatKind
::Box(..) => {
639 "box pattern syntax is experimental"
642 PatKind
::Range(_
, Some(_
), Spanned { node: RangeEnd::Excluded, .. }
) => {
645 exclusive_range_pattern
,
647 "exclusive range pattern syntax is experimental"
652 visit
::walk_pat(self, pattern
)
655 fn visit_fn(&mut self, fn_kind
: FnKind
<'a
>, span
: Span
, _
: NodeId
) {
656 if let Some(header
) = fn_kind
.header() {
657 // Stability of const fn methods are covered in `visit_assoc_item` below.
658 self.check_extern(header
.ext
, header
.constness
);
661 if fn_kind
.ctxt() != Some(FnCtxt
::Foreign
) && fn_kind
.decl().c_variadic() {
662 gate_feature_post
!(&self, c_variadic
, span
, "C-variadic functions are unstable");
665 visit
::walk_fn(self, fn_kind
, span
)
668 fn visit_assoc_constraint(&mut self, constraint
: &'a AssocConstraint
) {
669 if let AssocConstraintKind
::Bound { .. }
= constraint
.kind
{
672 associated_type_bounds
,
674 "associated type bounds are unstable"
677 visit
::walk_assoc_constraint(self, constraint
)
680 fn visit_assoc_item(&mut self, i
: &'a ast
::AssocItem
, ctxt
: AssocCtxt
) {
681 let is_fn
= match i
.kind
{
682 ast
::AssocItemKind
::Fn(_
) => true,
683 ast
::AssocItemKind
::TyAlias(box ast
::TyAlias { ref generics, ref ty, .. }
) => {
684 if let (Some(_
), AssocCtxt
::Trait
) = (ty
, ctxt
) {
687 associated_type_defaults
,
689 "associated type defaults are unstable"
692 if let Some(ty
) = ty
{
693 self.check_impl_trait(ty
);
695 self.check_gat(generics
, i
.span
);
700 if let ast
::Defaultness
::Default(_
) = i
.kind
.defaultness() {
701 // Limit `min_specialization` to only specializing functions.
704 |x
: &Features
| x
.specialization
|| (is_fn
&& x
.min_specialization
),
707 "specialization is unstable"
710 visit
::walk_assoc_item(self, i
, ctxt
)
714 pub fn check_crate(krate
: &ast
::Crate
, sess
: &Session
) {
715 maybe_stage_features(sess
, krate
);
716 check_incompatible_features(sess
);
717 let mut visitor
= PostExpansionVisitor { sess, features: &sess.features_untracked() }
;
719 let spans
= sess
.parse_sess
.gated_spans
.spans
.borrow();
720 macro_rules
! gate_all
{
721 ($gate
:ident
, $msg
:literal
, $help
:literal
) => {
722 if let Some(spans
) = spans
.get(&sym
::$gate
) {
724 gate_feature_post
!(&visitor
, $gate
, *span
, $msg
, $help
);
728 ($gate
:ident
, $msg
:literal
) => {
729 if let Some(spans
) = spans
.get(&sym
::$gate
) {
731 gate_feature_post
!(&visitor
, $gate
, *span
, $msg
);
738 "`if let` guards are experimental",
739 "you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`"
741 gate_all
!(let_chains
, "`let` expressions in this position are unstable");
744 "async closures are unstable",
745 "to use an async block, remove the `||`: `async {`"
747 gate_all
!(more_qualified_paths
, "usage of qualified paths in this context is experimental");
748 gate_all
!(generators
, "yield syntax is experimental");
749 gate_all
!(raw_ref_op
, "raw address of syntax is experimental");
750 gate_all
!(const_trait_impl
, "const trait impls are experimental");
751 gate_all
!(half_open_range_patterns
, "half-open range patterns are unstable");
752 gate_all
!(inline_const
, "inline-const is experimental");
753 gate_all
!(inline_const_pat
, "inline-const in pattern position is experimental");
754 gate_all
!(associated_const_equality
, "associated const equality is incomplete");
755 gate_all
!(yeet_expr
, "`do yeet` expression is experimental");
757 // All uses of `gate_all!` below this point were added in #65742,
758 // and subsequently disabled (with the non-early gating readded).
759 macro_rules
! gate_all
{
760 ($gate
:ident
, $msg
:literal
) => {
761 // FIXME(eddyb) do something more useful than always
762 // disabling these uses of early feature-gatings.
764 for span
in spans
.get(&sym
::$gate
).unwrap_or(&vec
![]) {
765 gate_feature_post
!(&visitor
, $gate
, *span
, $msg
);
771 gate_all
!(trait_alias
, "trait aliases are experimental");
772 gate_all
!(associated_type_bounds
, "associated type bounds are unstable");
773 gate_all
!(decl_macro
, "`macro` is experimental");
774 gate_all
!(box_patterns
, "box pattern syntax is experimental");
775 gate_all
!(exclusive_range_pattern
, "exclusive range pattern syntax is experimental");
776 gate_all
!(try_blocks
, "`try` blocks are unstable");
777 gate_all
!(label_break_value
, "labels on blocks are unstable");
778 gate_all
!(box_syntax
, "box expression syntax is experimental; you can call `Box::new` instead");
779 // To avoid noise about type ascription in common syntax errors,
780 // only emit if it is the *only* error. (Also check it last.)
781 if sess
.parse_sess
.span_diagnostic
.err_count() == 0 {
782 gate_all
!(type_ascription
, "type ascription is experimental");
785 visit
::walk_crate(&mut visitor
, krate
);
788 fn maybe_stage_features(sess
: &Session
, krate
: &ast
::Crate
) {
789 // checks if `#![feature]` has been used to enable any lang feature
790 // does not check the same for lib features unless there's at least one
791 // declared lang feature
792 use rustc_errors
::Applicability
;
794 if !sess
.opts
.unstable_features
.is_nightly_build() {
795 let lang_features
= &sess
.features_untracked().declared_lang_features
;
796 if lang_features
.len() == 0 {
799 for attr
in krate
.attrs
.iter().filter(|attr
| attr
.has_name(sym
::feature
)) {
800 let mut err
= struct_span_err
!(
801 sess
.parse_sess
.span_diagnostic
,
804 "`#![feature]` may not be used on the {} release channel",
805 option_env
!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)")
807 let mut all_stable
= true;
809 attr
.meta_item_list().into_iter().flatten().flat_map(|nested
| nested
.ident())
811 let name
= ident
.name
;
812 let stable_since
= lang_features
814 .flat_map(|&(feature
, _
, since
)| if feature
== name { since }
else { None }
)
816 if let Some(since
) = stable_since
{
818 "the feature `{}` has been stable since {} and no longer requires \
819 an attribute to enable",
829 "remove the attribute",
831 Applicability
::MachineApplicable
,
839 fn check_incompatible_features(sess
: &Session
) {
840 let features
= sess
.features_untracked();
842 let declared_features
= features
843 .declared_lang_features
846 .map(|(name
, span
, _
)| (name
, span
))
847 .chain(features
.declared_lib_features
.iter().copied());
849 for (f1
, f2
) in rustc_feature
::INCOMPATIBLE_FEATURES
851 .filter(|&&(f1
, f2
)| features
.enabled(f1
) && features
.enabled(f2
))
853 if let Some((f1_name
, f1_span
)) = declared_features
.clone().find(|(name
, _
)| name
== f1
) {
854 if let Some((f2_name
, f2_span
)) = declared_features
.clone().find(|(name
, _
)| name
== f2
)
856 let spans
= vec
![f1_span
, f2_span
];
857 sess
.struct_span_err(
860 "features `{}` and `{}` are incompatible, using them at the same time \
865 .help("remove one of these features")