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 "PTX ABIs are experimental and subject to change"
129 "unadjusted ABI is an implementation detail and perma-unstable"
132 "msp430-interrupt" => {
135 abi_msp430_interrupt
,
137 "msp430-interrupt ABI is experimental and subject to change"
145 "x86-interrupt ABI is experimental and subject to change"
153 "amdgpu-kernel ABI is experimental and subject to change"
156 "avr-interrupt" | "avr-non-blocking-interrupt" => {
161 "avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change"
169 "efiapi ABI is experimental and subject to change"
172 "C-cmse-nonsecure-call" => {
175 abi_c_cmse_nonsecure_call
,
177 "C-cmse-nonsecure-call ABI is experimental and subject to change"
185 "C-unwind ABI is experimental and subject to change"
188 "stdcall-unwind" => {
193 "stdcall-unwind ABI is experimental and subject to change"
201 "system-unwind ABI is experimental and subject to change"
204 "thiscall-unwind" => {
209 "thiscall-unwind ABI is experimental and subject to change"
217 "cdecl-unwind ABI is experimental and subject to change"
220 "fastcall-unwind" => {
225 "fastcall-unwind ABI is experimental and subject to change"
228 "vectorcall-unwind" => {
233 "vectorcall-unwind ABI is experimental and subject to change"
241 "aapcs-unwind ABI is experimental and subject to change"
249 "win64-unwind ABI is experimental and subject to change"
257 "sysv64-unwind ABI is experimental and subject to change"
265 "wasm ABI is experimental and subject to change"
269 self.sess
.parse_sess
.span_diagnostic
.delay_span_bug(
271 &format
!("unrecognized ABI not caught in lowering: {}", abi
),
277 fn check_extern(&self, ext
: ast
::Extern
, constness
: ast
::Const
) {
278 if let ast
::Extern
::Explicit(abi
) = ext
{
279 self.check_abi(abi
, constness
);
283 fn maybe_report_invalid_custom_discriminants(&self, variants
: &[ast
::Variant
]) {
284 let has_fields
= variants
.iter().any(|variant
| match variant
.data
{
285 VariantData
::Tuple(..) | VariantData
::Struct(..) => true,
286 VariantData
::Unit(..) => false,
289 let discriminant_spans
= variants
291 .filter(|variant
| match variant
.data
{
292 VariantData
::Tuple(..) | VariantData
::Struct(..) => false,
293 VariantData
::Unit(..) => true,
295 .filter_map(|variant
| variant
.disr_expr
.as_ref().map(|c
| c
.value
.span
))
296 .collect
::<Vec
<_
>>();
298 if !discriminant_spans
.is_empty() && has_fields
{
299 let mut err
= feature_err(
300 &self.sess
.parse_sess
,
301 sym
::arbitrary_enum_discriminant
,
302 discriminant_spans
.clone(),
303 "custom discriminant values are not allowed in enums with tuple or struct variants",
305 for sp
in discriminant_spans
{
306 err
.span_label(sp
, "disallowed custom discriminant");
308 for variant
in variants
.iter() {
309 match &variant
.data
{
310 VariantData
::Struct(..) => {
311 err
.span_label(variant
.span
, "struct variant defined here");
313 VariantData
::Tuple(..) => {
314 err
.span_label(variant
.span
, "tuple variant defined here");
316 VariantData
::Unit(..) => {}
323 fn check_gat(&self, generics
: &ast
::Generics
, span
: Span
) {
324 if !generics
.params
.is_empty() {
327 generic_associated_types
,
329 "generic associated types are unstable"
332 if !generics
.where_clause
.predicates
.is_empty() {
335 generic_associated_types
,
337 "where clauses on associated types are unstable"
342 /// Feature gate `impl Trait` inside `type Alias = $type_expr;`.
343 fn check_impl_trait(&self, ty
: &ast
::Ty
) {
344 struct ImplTraitVisitor
<'a
> {
345 vis
: &'a PostExpansionVisitor
<'a
>,
347 impl Visitor
<'_
> for ImplTraitVisitor
<'_
> {
348 fn visit_ty(&mut self, ty
: &ast
::Ty
) {
349 if let ast
::TyKind
::ImplTrait(..) = ty
.kind
{
352 type_alias_impl_trait
,
354 "`impl Trait` in type aliases is unstable"
357 visit
::walk_ty(self, ty
);
360 ImplTraitVisitor { vis: self }
.visit_ty(ty
);
364 impl<'a
> Visitor
<'a
> for PostExpansionVisitor
<'a
> {
365 fn visit_attribute(&mut self, attr
: &ast
::Attribute
) {
366 let attr_info
= attr
.ident().and_then(|ident
| BUILTIN_ATTRIBUTE_MAP
.get(&ident
.name
));
367 // Check feature gates for built-in attributes.
368 if let Some(BuiltinAttribute
{
369 gate
: AttributeGate
::Gated(_
, name
, descr
, has_feature
),
373 gate_feature_fn
!(self, has_feature
, attr
.span
, *name
, descr
);
375 // Check unstable flavors of the `#[doc]` attribute.
376 if attr
.has_name(sym
::doc
) {
377 for nested_meta
in attr
.meta_item_list().unwrap_or_default() {
378 macro_rules
! gate_doc
{ ($
($name
:ident
=> $feature
:ident
)*) => {
379 $
(if nested_meta
.has_name(sym
::$name
) {
380 let msg
= concat
!("`#[doc(", stringify
!($name
), ")]` is experimental");
381 gate_feature_post
!(self, $feature
, attr
.span
, msg
);
387 cfg_hide
=> doc_cfg_hide
389 notable_trait
=> doc_notable_trait
392 if nested_meta
.has_name(sym
::keyword
) {
393 let msg
= "`#[doc(keyword)]` is meant for internal use only";
394 gate_feature_post
!(self, rustdoc_internals
, attr
.span
, msg
);
399 // Check for unstable modifiers on `#[link(..)]` attribute
400 if attr
.has_name(sym
::link
) {
401 for nested_meta
in attr
.meta_item_list().unwrap_or_default() {
402 if nested_meta
.has_name(sym
::modifiers
) {
403 if let Some(modifiers
) = nested_meta
.value_str() {
404 for modifier
in modifiers
.as_str().split('
,'
) {
405 if let Some(modifier
) = modifier
.strip_prefix(&['
+'
, '
-'
]) {
406 macro_rules
! gate_modifier
{ ($
($name
:literal
=> $feature
:ident
)*) => {
407 $
(if modifier
== $name
{
408 let msg
= concat
!("`#[link(modifiers=\"", $name
, "\")]` is unstable");
412 nested_meta
.name_value_literal_span().unwrap(),
419 "bundle" => native_link_modifiers_bundle
420 "verbatim" => native_link_modifiers_verbatim
421 "as-needed" => native_link_modifiers_as_needed
430 // Emit errors for non-staged-api crates.
431 if !self.features
.staged_api
{
432 if attr
.has_name(sym
::rustc_deprecated
)
433 || attr
.has_name(sym
::unstable
)
434 || attr
.has_name(sym
::stable
)
435 || attr
.has_name(sym
::rustc_const_unstable
)
436 || attr
.has_name(sym
::rustc_const_stable
)
442 "stability attributes may not be used outside of the standard library",
449 fn visit_item(&mut self, i
: &'a ast
::Item
) {
451 ast
::ItemKind
::ForeignMod(ref foreign_module
) => {
452 if let Some(abi
) = foreign_module
.abi
{
453 self.check_abi(abi
, ast
::Const
::No
);
457 ast
::ItemKind
::Fn(..) => {
458 if self.sess
.contains_name(&i
.attrs
, sym
::start
) {
463 "`#[start]` functions are experimental \
464 and their signature may change \
470 ast
::ItemKind
::Struct(..) => {
471 for attr
in self.sess
.filter_by_name(&i
.attrs
, sym
::repr
) {
472 for item
in attr
.meta_item_list().unwrap_or_else(Vec
::new
) {
473 if item
.has_name(sym
::simd
) {
478 "SIMD types are experimental and possibly buggy"
485 ast
::ItemKind
::Enum(ast
::EnumDef { ref variants, .. }
, ..) => {
486 for variant
in variants
{
487 match (&variant
.data
, &variant
.disr_expr
) {
488 (ast
::VariantData
::Unit(..), _
) => {}
489 (_
, Some(disr_expr
)) => gate_feature_post
!(
491 arbitrary_enum_discriminant
,
492 disr_expr
.value
.span
,
493 "discriminants on non-unit variants are experimental"
499 let has_feature
= self.features
.arbitrary_enum_discriminant
;
500 if !has_feature
&& !i
.span
.allows_unstable(sym
::arbitrary_enum_discriminant
) {
501 self.maybe_report_invalid_custom_discriminants(&variants
);
505 ast
::ItemKind
::Impl(box ast
::Impl { polarity, defaultness, ref of_trait, .. }
) => {
506 if let ast
::ImplPolarity
::Negative(span
) = polarity
{
510 span
.to(of_trait
.as_ref().map_or(span
, |t
| t
.path
.span
)),
511 "negative trait bounds are not yet fully implemented; \
512 use marker types for now"
516 if let ast
::Defaultness
::Default(_
) = defaultness
{
517 gate_feature_post
!(&self, specialization
, i
.span
, "specialization is unstable");
521 ast
::ItemKind
::Trait(box ast
::Trait { is_auto: ast::IsAuto::Yes, .. }
) => {
526 "auto traits are experimental and possibly buggy"
530 ast
::ItemKind
::TraitAlias(..) => {
531 gate_feature_post
!(&self, trait_alias
, i
.span
, "trait aliases are experimental");
534 ast
::ItemKind
::MacroDef(ast
::MacroDef { macro_rules: false, .. }
) => {
535 let msg
= "`macro` is experimental";
536 gate_feature_post
!(&self, decl_macro
, i
.span
, msg
);
539 ast
::ItemKind
::TyAlias(box ast
::TyAlias { ty: Some(ref ty), .. }
) => {
540 self.check_impl_trait(&ty
)
546 visit
::walk_item(self, i
);
549 fn visit_foreign_item(&mut self, i
: &'a ast
::ForeignItem
) {
551 ast
::ForeignItemKind
::Fn(..) | ast
::ForeignItemKind
::Static(..) => {
552 let link_name
= self.sess
.first_attr_value_str_by_name(&i
.attrs
, sym
::link_name
);
554 link_name
.map_or(false, |val
| val
.as_str().starts_with("llvm."));
558 link_llvm_intrinsics
,
560 "linking to LLVM intrinsics is experimental"
564 ast
::ForeignItemKind
::TyAlias(..) => {
565 gate_feature_post
!(&self, extern_types
, i
.span
, "extern types are experimental");
567 ast
::ForeignItemKind
::MacCall(..) => {}
570 visit
::walk_foreign_item(self, i
)
573 fn visit_ty(&mut self, ty
: &'a ast
::Ty
) {
575 ast
::TyKind
::BareFn(ref bare_fn_ty
) => {
576 // Function pointers cannot be `const`
577 self.check_extern(bare_fn_ty
.ext
, ast
::Const
::No
);
579 ast
::TyKind
::Never
=> {
580 gate_feature_post
!(&self, never_type
, ty
.span
, "the `!` type is experimental");
584 visit
::walk_ty(self, ty
)
587 fn visit_fn_ret_ty(&mut self, ret_ty
: &'a ast
::FnRetTy
) {
588 if let ast
::FnRetTy
::Ty(ref output_ty
) = *ret_ty
{
589 if let ast
::TyKind
::Never
= output_ty
.kind
{
592 self.visit_ty(output_ty
)
597 fn visit_expr(&mut self, e
: &'a ast
::Expr
) {
599 ast
::ExprKind
::Box(_
) => {
604 "box expression syntax is experimental; you can call `Box::new` instead"
607 ast
::ExprKind
::Type(..) => {
608 // To avoid noise about type ascription in common syntax errors, only emit if it
609 // is the *only* error.
610 if self.sess
.parse_sess
.span_diagnostic
.err_count() == 0 {
615 "type ascription is experimental"
619 ast
::ExprKind
::TryBlock(_
) => {
620 gate_feature_post
!(&self, try_blocks
, e
.span
, "`try` expression is experimental");
622 ast
::ExprKind
::Block(_
, Some(label
)) => {
627 "labels on blocks are unstable"
632 visit
::walk_expr(self, e
)
635 fn visit_pat(&mut self, pattern
: &'a ast
::Pat
) {
636 match &pattern
.kind
{
637 PatKind
::Slice(pats
) => {
639 let inner_pat
= match &pat
.kind
{
640 PatKind
::Ident(.., Some(pat
)) => pat
,
643 if let PatKind
::Range(Some(_
), None
, Spanned { .. }
) = inner_pat
.kind
{
646 half_open_range_patterns
,
648 "`X..` patterns in slices are experimental"
653 PatKind
::Box(..) => {
658 "box pattern syntax is experimental"
661 PatKind
::Range(_
, Some(_
), Spanned { node: RangeEnd::Excluded, .. }
) => {
664 exclusive_range_pattern
,
666 "exclusive range pattern syntax is experimental"
671 visit
::walk_pat(self, pattern
)
674 fn visit_fn(&mut self, fn_kind
: FnKind
<'a
>, span
: Span
, _
: NodeId
) {
675 if let Some(header
) = fn_kind
.header() {
676 // Stability of const fn methods are covered in `visit_assoc_item` below.
677 self.check_extern(header
.ext
, header
.constness
);
680 if fn_kind
.ctxt() != Some(FnCtxt
::Foreign
) && fn_kind
.decl().c_variadic() {
681 gate_feature_post
!(&self, c_variadic
, span
, "C-variadic functions are unstable");
684 visit
::walk_fn(self, fn_kind
, span
)
687 fn visit_assoc_constraint(&mut self, constraint
: &'a AssocConstraint
) {
688 if let AssocConstraintKind
::Bound { .. }
= constraint
.kind
{
691 associated_type_bounds
,
693 "associated type bounds are unstable"
696 visit
::walk_assoc_constraint(self, constraint
)
699 fn visit_assoc_item(&mut self, i
: &'a ast
::AssocItem
, ctxt
: AssocCtxt
) {
700 let is_fn
= match i
.kind
{
701 ast
::AssocItemKind
::Fn(_
) => true,
702 ast
::AssocItemKind
::TyAlias(box ast
::TyAlias { ref generics, ref ty, .. }
) => {
703 if let (Some(_
), AssocCtxt
::Trait
) = (ty
, ctxt
) {
706 associated_type_defaults
,
708 "associated type defaults are unstable"
711 if let Some(ty
) = ty
{
712 self.check_impl_trait(ty
);
714 self.check_gat(generics
, i
.span
);
719 if let ast
::Defaultness
::Default(_
) = i
.kind
.defaultness() {
720 // Limit `min_specialization` to only specializing functions.
723 |x
: &Features
| x
.specialization
|| (is_fn
&& x
.min_specialization
),
726 "specialization is unstable"
729 visit
::walk_assoc_item(self, i
, ctxt
)
732 fn visit_vis(&mut self, vis
: &'a ast
::Visibility
) {
733 if let ast
::VisibilityKind
::Crate(ast
::CrateSugar
::JustCrate
) = vis
.kind
{
736 crate_visibility_modifier
,
738 "`crate` visibility modifier is experimental"
741 visit
::walk_vis(self, vis
)
745 pub fn check_crate(krate
: &ast
::Crate
, sess
: &Session
) {
746 maybe_stage_features(sess
, krate
);
747 check_incompatible_features(sess
);
748 let mut visitor
= PostExpansionVisitor { sess, features: &sess.features_untracked() }
;
750 let spans
= sess
.parse_sess
.gated_spans
.spans
.borrow();
751 macro_rules
! gate_all
{
752 ($gate
:ident
, $msg
:literal
, $help
:literal
) => {
753 if let Some(spans
) = spans
.get(&sym
::$gate
) {
755 gate_feature_post
!(&visitor
, $gate
, *span
, $msg
, $help
);
759 ($gate
:ident
, $msg
:literal
) => {
760 if let Some(spans
) = spans
.get(&sym
::$gate
) {
762 gate_feature_post
!(&visitor
, $gate
, *span
, $msg
);
769 "`if let` guards are experimental",
770 "you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`"
772 gate_all
!(let_chains
, "`let` expressions in this position are unstable");
775 "async closures are unstable",
776 "to use an async block, remove the `||`: `async {`"
778 gate_all
!(more_qualified_paths
, "usage of qualified paths in this context is experimental");
779 gate_all
!(generators
, "yield syntax is experimental");
780 gate_all
!(raw_ref_op
, "raw address of syntax is experimental");
781 gate_all
!(const_trait_impl
, "const trait impls are experimental");
782 gate_all
!(half_open_range_patterns
, "half-open range patterns are unstable");
783 gate_all
!(inline_const
, "inline-const is experimental");
784 gate_all
!(inline_const_pat
, "inline-const in pattern position is experimental");
785 gate_all
!(associated_const_equality
, "associated const equality is incomplete");
786 gate_all
!(yeet_expr
, "`do yeet` expression is experimental");
788 // All uses of `gate_all!` below this point were added in #65742,
789 // and subsequently disabled (with the non-early gating readded).
790 macro_rules
! gate_all
{
791 ($gate
:ident
, $msg
:literal
) => {
792 // FIXME(eddyb) do something more useful than always
793 // disabling these uses of early feature-gatings.
795 for span
in spans
.get(&sym
::$gate
).unwrap_or(&vec
![]) {
796 gate_feature_post
!(&visitor
, $gate
, *span
, $msg
);
802 gate_all
!(trait_alias
, "trait aliases are experimental");
803 gate_all
!(associated_type_bounds
, "associated type bounds are unstable");
804 gate_all
!(crate_visibility_modifier
, "`crate` visibility modifier is experimental");
805 gate_all
!(decl_macro
, "`macro` is experimental");
806 gate_all
!(box_patterns
, "box pattern syntax is experimental");
807 gate_all
!(exclusive_range_pattern
, "exclusive range pattern syntax is experimental");
808 gate_all
!(try_blocks
, "`try` blocks are unstable");
809 gate_all
!(label_break_value
, "labels on blocks are unstable");
810 gate_all
!(box_syntax
, "box expression syntax is experimental; you can call `Box::new` instead");
811 // To avoid noise about type ascription in common syntax errors,
812 // only emit if it is the *only* error. (Also check it last.)
813 if sess
.parse_sess
.span_diagnostic
.err_count() == 0 {
814 gate_all
!(type_ascription
, "type ascription is experimental");
817 visit
::walk_crate(&mut visitor
, krate
);
820 fn maybe_stage_features(sess
: &Session
, krate
: &ast
::Crate
) {
821 // checks if `#![feature]` has been used to enable any lang feature
822 // does not check the same for lib features unless there's at least one
823 // declared lang feature
824 use rustc_errors
::Applicability
;
826 if !sess
.opts
.unstable_features
.is_nightly_build() {
827 let lang_features
= &sess
.features_untracked().declared_lang_features
;
828 if lang_features
.len() == 0 {
831 for attr
in krate
.attrs
.iter().filter(|attr
| attr
.has_name(sym
::feature
)) {
832 let mut err
= struct_span_err
!(
833 sess
.parse_sess
.span_diagnostic
,
836 "`#![feature]` may not be used on the {} release channel",
837 option_env
!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)")
839 let mut all_stable
= true;
841 attr
.meta_item_list().into_iter().flatten().flat_map(|nested
| nested
.ident())
843 let name
= ident
.name
;
844 let stable_since
= lang_features
846 .flat_map(|&(feature
, _
, since
)| if feature
== name { since }
else { None }
)
848 if let Some(since
) = stable_since
{
850 "the feature `{}` has been stable since {} and no longer requires \
851 an attribute to enable",
861 "remove the attribute",
863 Applicability
::MachineApplicable
,
871 fn check_incompatible_features(sess
: &Session
) {
872 let features
= sess
.features_untracked();
874 let declared_features
= features
875 .declared_lang_features
878 .map(|(name
, span
, _
)| (name
, span
))
879 .chain(features
.declared_lib_features
.iter().copied());
881 for (f1
, f2
) in rustc_feature
::INCOMPATIBLE_FEATURES
883 .filter(|&&(f1
, f2
)| features
.enabled(f1
) && features
.enabled(f2
))
885 if let Some((f1_name
, f1_span
)) = declared_features
.clone().find(|(name
, _
)| name
== f1
) {
886 if let Some((f2_name
, f2_span
)) = declared_features
.clone().find(|(name
, _
)| name
== f2
)
888 let spans
= vec
![f1_span
, f2_span
];
889 sess
.struct_span_err(
892 "features `{}` and `{}` are incompatible, using them at the same time \
897 .help("remove one of these features")