2 use rustc_ast
::visit
::{self, AssocCtxt, FnCtxt, FnKind, Visitor}
;
3 use rustc_ast
::{AssocTyConstraint, AssocTyConstraintKind, NodeId}
;
4 use rustc_ast
::{PatKind, RangeEnd, VariantData}
;
5 use rustc_errors
::struct_span_err
;
6 use rustc_feature
::{AttributeGate, 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, Symbol}
;
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
) {
62 let ast
::StrLit { symbol_unescaped, span, .. }
= abi
;
64 match &*symbol_unescaped
.as_str() {
66 "Rust" | "C" | "cdecl" | "stdcall" | "fastcall" | "aapcs" | "win64" | "sysv64"
69 gate_feature_post
!(&self, intrinsics
, span
, "intrinsics are subject to change");
71 "platform-intrinsic" => {
76 "platform intrinsics are experimental and possibly buggy"
84 "vectorcall is experimental and subject to change"
92 "thiscall is experimental and subject to change"
100 "rust-call ABI is subject to change"
108 "PTX ABIs are experimental and subject to change"
116 "unadjusted ABI is an implementation detail and perma-unstable"
119 "msp430-interrupt" => {
122 abi_msp430_interrupt
,
124 "msp430-interrupt ABI is experimental and subject to change"
132 "x86-interrupt ABI is experimental and subject to change"
140 "amdgpu-kernel ABI is experimental and subject to change"
143 "avr-interrupt" | "avr-non-blocking-interrupt" => {
148 "avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change"
156 "efiapi ABI is experimental and subject to change"
159 "C-cmse-nonsecure-call" => {
162 abi_c_cmse_nonsecure_call
,
164 "C-cmse-nonsecure-call ABI is experimental and subject to change"
171 .delay_span_bug(span
, &format
!("unrecognized ABI not caught in lowering: {}", abi
)),
175 fn check_extern(&self, ext
: ast
::Extern
) {
176 if let ast
::Extern
::Explicit(abi
) = ext
{
181 fn maybe_report_invalid_custom_discriminants(&self, variants
: &[ast
::Variant
]) {
182 let has_fields
= variants
.iter().any(|variant
| match variant
.data
{
183 VariantData
::Tuple(..) | VariantData
::Struct(..) => true,
184 VariantData
::Unit(..) => false,
187 let discriminant_spans
= variants
189 .filter(|variant
| match variant
.data
{
190 VariantData
::Tuple(..) | VariantData
::Struct(..) => false,
191 VariantData
::Unit(..) => true,
193 .filter_map(|variant
| variant
.disr_expr
.as_ref().map(|c
| c
.value
.span
))
194 .collect
::<Vec
<_
>>();
196 if !discriminant_spans
.is_empty() && has_fields
{
197 let mut err
= feature_err(
198 &self.sess
.parse_sess
,
199 sym
::arbitrary_enum_discriminant
,
200 discriminant_spans
.clone(),
201 "custom discriminant values are not allowed in enums with tuple or struct variants",
203 for sp
in discriminant_spans
{
204 err
.span_label(sp
, "disallowed custom discriminant");
206 for variant
in variants
.iter() {
207 match &variant
.data
{
208 VariantData
::Struct(..) => {
209 err
.span_label(variant
.span
, "struct variant defined here");
211 VariantData
::Tuple(..) => {
212 err
.span_label(variant
.span
, "tuple variant defined here");
214 VariantData
::Unit(..) => {}
221 fn check_gat(&self, generics
: &ast
::Generics
, span
: Span
) {
222 if !generics
.params
.is_empty() {
225 generic_associated_types
,
227 "generic associated types are unstable"
230 if !generics
.where_clause
.predicates
.is_empty() {
233 generic_associated_types
,
235 "where clauses on associated types are unstable"
240 /// Feature gate `impl Trait` inside `type Alias = $type_expr;`.
241 fn check_impl_trait(&self, ty
: &ast
::Ty
) {
242 struct ImplTraitVisitor
<'a
> {
243 vis
: &'a PostExpansionVisitor
<'a
>,
245 impl Visitor
<'_
> for ImplTraitVisitor
<'_
> {
246 fn visit_ty(&mut self, ty
: &ast
::Ty
) {
247 if let ast
::TyKind
::ImplTrait(..) = ty
.kind
{
250 type_alias_impl_trait
,
252 "`impl Trait` in type aliases is unstable"
255 visit
::walk_ty(self, ty
);
258 ImplTraitVisitor { vis: self }
.visit_ty(ty
);
262 impl<'a
> Visitor
<'a
> for PostExpansionVisitor
<'a
> {
263 fn visit_attribute(&mut self, attr
: &ast
::Attribute
) {
265 attr
.ident().and_then(|ident
| BUILTIN_ATTRIBUTE_MAP
.get(&ident
.name
)).map(|a
| **a
);
266 // Check feature gates for built-in attributes.
267 if let Some((.., AttributeGate
::Gated(_
, name
, descr
, has_feature
))) = attr_info
{
268 gate_feature_fn
!(self, has_feature
, attr
.span
, name
, descr
);
270 // Check unstable flavors of the `#[doc]` attribute.
271 if self.sess
.check_name(attr
, sym
::doc
) {
272 for nested_meta
in attr
.meta_item_list().unwrap_or_default() {
273 macro_rules
! gate_doc
{ ($
($name
:ident
=> $feature
:ident
)*) => {
274 $
(if nested_meta
.has_name(sym
::$name
) {
275 let msg
= concat
!("`#[doc(", stringify
!($name
), ")]` is experimental");
276 gate_feature_post
!(self, $feature
, attr
.span
, msg
);
281 include
=> external_doc
284 spotlight
=> doc_spotlight
285 keyword
=> doc_keyword
291 fn visit_name(&mut self, sp
: Span
, name
: Symbol
) {
292 if !name
.as_str().is_ascii() {
296 self.sess
.parse_sess
.source_map().guess_head_span(sp
),
297 "non-ascii idents are not fully supported"
302 fn visit_item(&mut self, i
: &'a ast
::Item
) {
304 ast
::ItemKind
::ForeignMod(ref foreign_module
) => {
305 if let Some(abi
) = foreign_module
.abi
{
310 ast
::ItemKind
::Fn(..) => {
311 if self.sess
.contains_name(&i
.attrs
[..], sym
::plugin_registrar
) {
316 "compiler plugins are experimental and possibly buggy"
319 if self.sess
.contains_name(&i
.attrs
[..], sym
::start
) {
324 "`#[start]` functions are experimental \
325 and their signature may change \
329 if self.sess
.contains_name(&i
.attrs
[..], sym
::main
) {
334 "declaration of a non-standard `#[main]` \
335 function may change over time, for now \
336 a top-level `fn main()` is required"
341 ast
::ItemKind
::Struct(..) => {
342 for attr
in self.sess
.filter_by_name(&i
.attrs
[..], sym
::repr
) {
343 for item
in attr
.meta_item_list().unwrap_or_else(Vec
::new
) {
344 if item
.has_name(sym
::simd
) {
349 "SIMD types are experimental and possibly buggy"
356 ast
::ItemKind
::Enum(ast
::EnumDef { ref variants, .. }
, ..) => {
357 for variant
in variants
{
358 match (&variant
.data
, &variant
.disr_expr
) {
359 (ast
::VariantData
::Unit(..), _
) => {}
360 (_
, Some(disr_expr
)) => gate_feature_post
!(
362 arbitrary_enum_discriminant
,
363 disr_expr
.value
.span
,
364 "discriminants on non-unit variants are experimental"
370 let has_feature
= self.features
.arbitrary_enum_discriminant
;
371 if !has_feature
&& !i
.span
.allows_unstable(sym
::arbitrary_enum_discriminant
) {
372 self.maybe_report_invalid_custom_discriminants(&variants
);
376 ast
::ItemKind
::Impl(box ast
::ImplKind
{
377 polarity
, defaultness
, ref of_trait
, ..
379 if let ast
::ImplPolarity
::Negative(span
) = polarity
{
383 span
.to(of_trait
.as_ref().map_or(span
, |t
| t
.path
.span
)),
384 "negative trait bounds are not yet fully implemented; \
385 use marker types for now"
389 if let ast
::Defaultness
::Default(_
) = defaultness
{
390 gate_feature_post
!(&self, specialization
, i
.span
, "specialization is unstable");
394 ast
::ItemKind
::Trait(box ast
::TraitKind(ast
::IsAuto
::Yes
, ..)) => {
399 "auto traits are experimental and possibly buggy"
403 ast
::ItemKind
::TraitAlias(..) => {
404 gate_feature_post
!(&self, trait_alias
, i
.span
, "trait aliases are experimental");
407 ast
::ItemKind
::MacroDef(ast
::MacroDef { macro_rules: false, .. }
) => {
408 let msg
= "`macro` is experimental";
409 gate_feature_post
!(&self, decl_macro
, i
.span
, msg
);
412 ast
::ItemKind
::TyAlias(box ast
::TyAliasKind(_
, _
, _
, Some(ref ty
))) => {
413 self.check_impl_trait(&ty
)
419 visit
::walk_item(self, i
);
422 fn visit_foreign_item(&mut self, i
: &'a ast
::ForeignItem
) {
424 ast
::ForeignItemKind
::Fn(..) | ast
::ForeignItemKind
::Static(..) => {
425 let link_name
= self.sess
.first_attr_value_str_by_name(&i
.attrs
, sym
::link_name
);
427 link_name
.map_or(false, |val
| val
.as_str().starts_with("llvm."));
431 link_llvm_intrinsics
,
433 "linking to LLVM intrinsics is experimental"
437 ast
::ForeignItemKind
::TyAlias(..) => {
438 gate_feature_post
!(&self, extern_types
, i
.span
, "extern types are experimental");
440 ast
::ForeignItemKind
::MacCall(..) => {}
443 visit
::walk_foreign_item(self, i
)
446 fn visit_ty(&mut self, ty
: &'a ast
::Ty
) {
448 ast
::TyKind
::BareFn(ref bare_fn_ty
) => {
449 self.check_extern(bare_fn_ty
.ext
);
451 ast
::TyKind
::Never
=> {
452 gate_feature_post
!(&self, never_type
, ty
.span
, "the `!` type is experimental");
456 visit
::walk_ty(self, ty
)
459 fn visit_fn_ret_ty(&mut self, ret_ty
: &'a ast
::FnRetTy
) {
460 if let ast
::FnRetTy
::Ty(ref output_ty
) = *ret_ty
{
461 if let ast
::TyKind
::Never
= output_ty
.kind
{
464 self.visit_ty(output_ty
)
469 fn visit_expr(&mut self, e
: &'a ast
::Expr
) {
471 ast
::ExprKind
::Box(_
) => {
476 "box expression syntax is experimental; you can call `Box::new` instead"
479 ast
::ExprKind
::Type(..) => {
480 // To avoid noise about type ascription in common syntax errors, only emit if it
481 // is the *only* error.
482 if self.sess
.parse_sess
.span_diagnostic
.err_count() == 0 {
487 "type ascription is experimental"
491 ast
::ExprKind
::TryBlock(_
) => {
492 gate_feature_post
!(&self, try_blocks
, e
.span
, "`try` expression is experimental");
494 ast
::ExprKind
::Block(_
, opt_label
) => {
495 if let Some(label
) = opt_label
{
500 "labels on blocks are unstable"
506 visit
::walk_expr(self, e
)
509 fn visit_pat(&mut self, pattern
: &'a ast
::Pat
) {
510 match &pattern
.kind
{
511 PatKind
::Box(..) => {
516 "box pattern syntax is experimental"
519 PatKind
::Range(_
, _
, Spanned { node: RangeEnd::Excluded, .. }
) => {
522 exclusive_range_pattern
,
524 "exclusive range pattern syntax is experimental"
529 visit
::walk_pat(self, pattern
)
532 fn visit_fn(&mut self, fn_kind
: FnKind
<'a
>, span
: Span
, _
: NodeId
) {
533 if let Some(header
) = fn_kind
.header() {
534 // Stability of const fn methods are covered in `visit_assoc_item` below.
535 self.check_extern(header
.ext
);
537 if let (ast
::Const
::Yes(_
), ast
::Extern
::Implicit
)
538 | (ast
::Const
::Yes(_
), ast
::Extern
::Explicit(_
)) = (header
.constness
, header
.ext
)
544 "`const extern fn` definitions are unstable"
549 if fn_kind
.ctxt() != Some(FnCtxt
::Foreign
) && fn_kind
.decl().c_variadic() {
550 gate_feature_post
!(&self, c_variadic
, span
, "C-variadic functions are unstable");
553 visit
::walk_fn(self, fn_kind
, span
)
556 fn visit_assoc_ty_constraint(&mut self, constraint
: &'a AssocTyConstraint
) {
557 if let AssocTyConstraintKind
::Bound { .. }
= constraint
.kind
{
560 associated_type_bounds
,
562 "associated type bounds are unstable"
565 visit
::walk_assoc_ty_constraint(self, constraint
)
568 fn visit_assoc_item(&mut self, i
: &'a ast
::AssocItem
, ctxt
: AssocCtxt
) {
569 let is_fn
= match i
.kind
{
570 ast
::AssocItemKind
::Fn(box ast
::FnKind(_
, ref sig
, _
, _
)) => {
571 if let (ast
::Const
::Yes(_
), AssocCtxt
::Trait
) = (sig
.header
.constness
, ctxt
) {
572 gate_feature_post
!(&self, const_fn
, i
.span
, "const fn is unstable");
576 ast
::AssocItemKind
::TyAlias(box ast
::TyAliasKind(_
, ref generics
, _
, ref ty
)) => {
577 if let (Some(_
), AssocCtxt
::Trait
) = (ty
, ctxt
) {
580 associated_type_defaults
,
582 "associated type defaults are unstable"
585 if let Some(ty
) = ty
{
586 self.check_impl_trait(ty
);
588 self.check_gat(generics
, i
.span
);
593 if let ast
::Defaultness
::Default(_
) = i
.kind
.defaultness() {
594 // Limit `min_specialization` to only specializing functions.
597 |x
: &Features
| x
.specialization
|| (is_fn
&& x
.min_specialization
),
600 "specialization is unstable"
603 visit
::walk_assoc_item(self, i
, ctxt
)
606 fn visit_vis(&mut self, vis
: &'a ast
::Visibility
) {
607 if let ast
::VisibilityKind
::Crate(ast
::CrateSugar
::JustCrate
) = vis
.kind
{
610 crate_visibility_modifier
,
612 "`crate` visibility modifier is experimental"
615 visit
::walk_vis(self, vis
)
619 pub fn check_crate(krate
: &ast
::Crate
, sess
: &Session
) {
620 maybe_stage_features(sess
, krate
);
621 check_incompatible_features(sess
);
622 let mut visitor
= PostExpansionVisitor { sess, features: &sess.features_untracked() }
;
624 let spans
= sess
.parse_sess
.gated_spans
.spans
.borrow();
625 macro_rules
! gate_all
{
626 ($gate
:ident
, $msg
:literal
, $help
:literal
) => {
627 if let Some(spans
) = spans
.get(&sym
::$gate
) {
629 gate_feature_post
!(&visitor
, $gate
, *span
, $msg
, $help
);
633 ($gate
:ident
, $msg
:literal
) => {
634 if let Some(spans
) = spans
.get(&sym
::$gate
) {
636 gate_feature_post
!(&visitor
, $gate
, *span
, $msg
);
641 gate_all
!(if_let_guard
, "`if let` guards are experimental");
642 gate_all
!(let_chains
, "`let` expressions in this position are experimental");
645 "async closures are unstable",
646 "to use an async block, remove the `||`: `async {`"
648 gate_all
!(generators
, "yield syntax is experimental");
649 gate_all
!(or_patterns
, "or-patterns syntax is experimental");
650 gate_all
!(raw_ref_op
, "raw address of syntax is experimental");
651 gate_all
!(const_trait_bound_opt_out
, "`?const` on trait bounds is experimental");
652 gate_all
!(const_trait_impl
, "const trait impls are experimental");
653 gate_all
!(half_open_range_patterns
, "half-open range patterns are unstable");
654 gate_all
!(inline_const
, "inline-const is experimental");
656 extended_key_value_attributes
,
657 "arbitrary expressions in key-value attributes are unstable"
660 const_generics_defaults
,
661 "default values for const generic parameters are experimental"
663 if sess
.parse_sess
.span_diagnostic
.err_count() == 0 {
664 // Errors for `destructuring_assignment` can get quite noisy, especially where `_` is
665 // involved, so we only emit errors where there are no other parsing errors.
666 gate_all
!(destructuring_assignment
, "destructuring assignments are unstable");
669 // All uses of `gate_all!` below this point were added in #65742,
670 // and subsequently disabled (with the non-early gating readded).
671 macro_rules
! gate_all
{
672 ($gate
:ident
, $msg
:literal
) => {
673 // FIXME(eddyb) do something more useful than always
674 // disabling these uses of early feature-gatings.
676 for span
in spans
.get(&sym
::$gate
).unwrap_or(&vec
![]) {
677 gate_feature_post
!(&visitor
, $gate
, *span
, $msg
);
683 gate_all
!(trait_alias
, "trait aliases are experimental");
684 gate_all
!(associated_type_bounds
, "associated type bounds are unstable");
685 gate_all
!(crate_visibility_modifier
, "`crate` visibility modifier is experimental");
686 gate_all
!(const_generics
, "const generics are unstable");
687 gate_all
!(decl_macro
, "`macro` is experimental");
688 gate_all
!(box_patterns
, "box pattern syntax is experimental");
689 gate_all
!(exclusive_range_pattern
, "exclusive range pattern syntax is experimental");
690 gate_all
!(try_blocks
, "`try` blocks are unstable");
691 gate_all
!(label_break_value
, "labels on blocks are unstable");
692 gate_all
!(box_syntax
, "box expression syntax is experimental; you can call `Box::new` instead");
693 // To avoid noise about type ascription in common syntax errors,
694 // only emit if it is the *only* error. (Also check it last.)
695 if sess
.parse_sess
.span_diagnostic
.err_count() == 0 {
696 gate_all
!(type_ascription
, "type ascription is experimental");
699 visit
::walk_crate(&mut visitor
, krate
);
702 fn maybe_stage_features(sess
: &Session
, krate
: &ast
::Crate
) {
703 if !sess
.opts
.unstable_features
.is_nightly_build() {
704 for attr
in krate
.attrs
.iter().filter(|attr
| sess
.check_name(attr
, sym
::feature
)) {
706 sess
.parse_sess
.span_diagnostic
,
709 "`#![feature]` may not be used on the {} release channel",
710 option_env
!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)")
717 fn check_incompatible_features(sess
: &Session
) {
718 let features
= sess
.features_untracked();
720 let declared_features
= features
721 .declared_lang_features
724 .map(|(name
, span
, _
)| (name
, span
))
725 .chain(features
.declared_lib_features
.iter().copied());
727 for (f1
, f2
) in rustc_feature
::INCOMPATIBLE_FEATURES
729 .filter(|&&(f1
, f2
)| features
.enabled(f1
) && features
.enabled(f2
))
731 if let Some((f1_name
, f1_span
)) = declared_features
.clone().find(|(name
, _
)| name
== f1
) {
732 if let Some((f2_name
, f2_span
)) = declared_features
.clone().find(|(name
, _
)| name
== f2
)
734 let spans
= vec
![f1_span
, f2_span
];
735 sess
.struct_span_err(
738 "features `{}` and `{}` are incompatible, using them at the same time \
743 .help("remove one of these features")